StreamData: data generation and property testing for Elixir

How could it know? Can elixir distinguish characters from integers? I think it can’t…

And that shrink isn’t actually very impressive. The minimum example should have been the empty list, right?

It can’t know from the list itself, but it might be able from the generator used.
Also the shrink is correct. [] does not include 42, so the assert is fulfilled.

1 Like

Oh, sorry, ?* is 42. Read that in a hurry and thought it was trying to find a list that didn’t contain 42, instead of the one that does :stuck_out_tongue:

Thanks for StreamData and PropertyTest - going to prove very useful for me. I’ve taken it out for a spin but I can’t seem to influence the number of generations. My test looks like this:

  import PropertyTest
  property "checks that a date fits within the start and end dates for that year" do
    check all  day   <- StreamData.int(1..28),
               month <- StreamData.int(1..12),
               year  <- StreamData.int(1..3000),
               max_runs: 1_000
    do
      {:ok, date} = Date.new(year, month, day)
      starts = struct(Date, Cldr.Calendar.ISOWeek.first_day_of_year(date))
      ends = struct(Date, Cldr.Calendar.ISOWeek.last_day_of_year(date))
      assert Date.compare(starts, date) in [:lt, :eq] and Date.compare(ends, date) in [:gt, :eq]
    end
  end

I thought either the clauses would act like they do for a comprehension (seems they don’t, which makes sense) or would have been influenced by :max_runs. But no matter what I put for :max_runs the result of running this test is always 1 property, 125 tests, 0 failures. Any guidance would be much appreciated.

1 Like

Hey @kip, the “1 property” that you see when tests finish is just the number of properties that were run, not the number of times each single property was run. Actually a property is not related to check all so you can put a check all inside a test "something" and it will call it a test. :max_runs should be working in your example, to verify you can add something like IO.write("·")s in the body of the property and it will visually show you each time the body of a check all is tested.

1 Like

I’ve to say the way to compose generators with stream_data is really nice. I’ve created generators for the date/time structs of elixir (here) and some more custom ones and after a bit of getting used to them it’s quite simple to create them.

Also it’s great for DRYing up test setups. In a testcase I had tests for multiple different properties of returned values, but the setup was always the same, so I simply create a private helper function to hold the whole generator and just return the few values I needed for the tests.

    property "date pairs are always on the same day" do
      check all {from, until, _diff} <- consecutive_datetimes_with_date_diff() do
        # [some tests]
      end
    end
    # [ like 4 more of those ]

  defp consecutive_datetimes_with_date_diff do
    import StreamData

    gen all from <- date(),
            to_add <- integer(0..30),
            time_a <- time(),
            time_b <- time(),
            [time_from, time_to] = [time_a, time_b] |> Enum.sort do
      from = date_to_datetime(from, time_from)

      to =
        from
        |> Date.add(to_add)
        |> date_to_datetime(time_to)

      {from, to, to_add}
    end
  end
1 Like

FWIW, there are some ideas about automating generators from a single example in this library.

Has this already been included in Elixir? I can’t find it in the standard library, and I kinda stopped hearing about it after a while…

1 Like

José announced in his Elixir Conf keynote that it’ll stay a separate project and not being included into the core.

5 Likes