category
category

Reputation: 2155

doctests for randomly generated values

Given the following code:

defmodule Pullapi.Workout do                                                                                     
  import Pullapi.Numbers

  @moduledoc """                                                                                                 
  Functions that generate a workout representation                                                               
  """

  @doc """                                                                                                       
  Returns a pullup set defined by the number of `max_reps` a user can do, a `percentage`, and the                
  number of maximum additional or decremented reps, `rep_bound`.                                                 

  ## Examples                                                                                                    
  iex> Pullapi.Workout.pullup_set(20, 60, 5)                                                                     
  %{"Action" => "Pullups", "Units" => "14"}                                                                      
  """
  @spec pullup_set(integer, integer, integer) :: map()
  def pullup_set(max_reps, percentage, rep_bound) do
    median = max_reps * (percentage / 100)
    unit_range = Pullapi.Numbers.median_range(round(median), rep_bound)
    units = Enum.random(unit_range)

    %{"Action" => "Pullups", "Units" => "#{units}"}
  end
end

The doctest fails with:

  1) test doc at Pullapi.Workout.pullup_set/3 (1) (PullapiTest)
     test/pullapi_test.exs:4
     Doctest failed
     code: Pullapi.Workout.pullup_set(20, 60, 5) === %{"Action" => "Pullups", "Units" => "14"}
     left: %{"Action" => "Pullups", "Units" => "8"}
     stacktrace:
       lib/pullapi/workout.ex:13: Pullapi.Workout (module)

Is there a way of specifying that the "Units" value is randomly generated? It looks like I'm following the way Enum.random is doctested

Upvotes: 0

Views: 587

Answers (1)

Dogbert
Dogbert

Reputation: 222128

Enum.random's doctest is explicitly setting a seed value for the test, which makes the result of future calls to :rand functions deterministic.

iex(1)> for _ <- 1..10 do
...(1)>   :rand.seed(:exsplus, {101, 102, 103})
...(1)>   Enum.random([1, 2, 3])
...(1)> end
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2]

The person who wrote the tests most likely ran the functions once to check what values are returned after those seed values are set and then put them in the doctest. Unless the inner workings of :rand change, those seeds will keep producing the same values, which is good enough for doctests (you can always fix the tests if it breaks in future versions of Erlang).

So, to fix your doctest, you should execute this code once in iex (you can change the seed values if you want):

:rand.seed(:exsplus, {101, 102, 103})
Pullapi.Workout.pullup_set(20, 60, 5)

And then hardcode the returned values in your doctest. Your tests should now pass until Erlang's rand module's internals change.

Upvotes: 3

Related Questions