co0lsky
Request for code review - property based test
Greetings! I am relatively new to property-based testing. I am practicing writing property tests and hoping for a code review if any practitioner has any comments or suggestions on the test.
defmodule Academy.UserPropTest do
alias Academy.UserShim
use Academy.TestCase, async: true
use PropCheck
use PropCheck.StateM
require ExUnitProperties
# Generators
def first_name() do
StreamData.string(:alphanumeric, length: 10..20)
end
def email() do
StreamData.string(:alphanumeric, length: 10)
end
def email(s) do
attrs = Map.get(s, :existing)
StreamData.constant(attrs.email)
end
def uid() do
StreamData.string(:alphanumeric, length: 10)
end
def uid(s) do
attrs = Map.get(s, :existing)
StreamData.constant(attrs.uid)
end
def role() do
StreamData.member_of(["admin", "editor", "tester", "support", "user"])
end
def profile() do
StreamData.nonempty(StreamData.map_of(StreamData.constant("lang"), StreamData.string(:ascii)))
end
def attrs() do
Enum.take(
StreamData.fixed_map(%{
first_name: first_name(),
email: email(),
uid: uid(),
role: role(),
profile: profile()
}),
1
)
end
def attrs(s) do
Enum.take(
StreamData.fixed_map(%{
first_name: first_name(),
email: email(s),
uid: uid(),
role: role(),
profile: profile()
}),
1
)
end
# Helpers
def like_email(map, email) do
Enum.any?(
Map.values(map),
fn attrs -> attrs.email == email end
)
end
property "user stateful operations", [:verbose] do
forall cmds <- commands(__MODULE__) do
{history, state, result} = run_commands(__MODULE__, cmds)
(result == :ok)
|> aggregate(command_names(cmds))
|> when_fail(
IO.puts("""
History: #{inspect(history)}
State: #{inspect(state)}
Result: #{inspect(result)}
""")
)
end
end
# initial model value at system start. Should be deterministic.
def initial_state(), do: %{}
def command(state) do
always_possible = [
{:call, UserShim, :create, attrs()}
]
relies_on_state =
case Map.equal?(state, %{}) do
# no values yet
true ->
[]
# values from which to work
false ->
s = state
[
{:call, UserShim, :create_existing, attrs(s)}
]
end
oneof(always_possible ++ relies_on_state)
end
# Picks whether a command should be valid under the current state.
def precondition(s, {:call, _, :create, [attrs | _]}) do
not like_email(s, attrs.email)
end
# - all calls with known emails
def precondition(s, {:call, _mod, _fun, [attrs | _]}) do
like_email(s, attrs.email)
end
# Given the state *prior* to the call {:call, mod, fun, args},
# determine whether the result (coming from the actual system)
# makes sense.
def postcondition(_state, {_, _mod, :create, _args}, {:ok, _}) do
true
end
def postcondition(_state, {_, _mod, :create_existing, _args}, {:error, _}) do
true
end
# Assuming the postcondition for a call was true, update the model
# accordingly for the test to proceed
def next_state(
state,
_,
{:call, _, :create, [attrs]}
) do
Map.put(state, :existing, attrs)
end
def next_state(state, _res, {:call, _mod, _fun, _args}) do
new_state = state
new_state
end
end
defmodule Academy.UserShim do
def create(attrs) do
Academy.User.create(attrs)
end
def create_existing(attrs) do
Academy.User.create(attrs)
end
end
First Post!
dimitarvp
Nothing really wrong pokes me in the eye. Do you want to add to this code but are not able to? That second module is a bit strange though, what’s its purpose?
Popular in Questions
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
The Elixir Typespec docs show the following syntax for keyword lists in typespecs:
# ...
| [key: type] # keyword lis...
New
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
Hi,
I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
Hey,
Just curious what are the main benefits of Elixir compared to Clojure?
When is Elixir more useful than Clojure and vice versa?
Th...
New
I tried installing
elixir 1.11.2
erlang 23.3.4
via asdf in my zsh shell. Enabled the versions locally and globally.
When I list them ...
New
I’m currently trying to understand how to join three tables using Ecto. All the examples I’ve seen use 2, so maybe I’m just missing somet...
New
In AR this is so simple
@articles = current_user.articles
How to do in Ecto?
def index(conn, _params) do
current_user = conn.assig...
New
Other popular topics
In Ruby, I can go:
User.find_by(email: "foobar@email.com").update(email: "hello@email.com")
How can I do something similar in Elixir? ...
New
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
Hi all,
Trying to get some more clarity over utc_datetime and naive_datetime for Ecto:
https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this:
...
New
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode.
The solution seems to be, in a hyphena...
New
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine)
This is a plugin that adds support for Elixir to JetBrains IntelliJ...
New
As a follow up to my earlier question:
I have the code compiling and running but not getting a successful login from the rest server. ...
New
by Lance Halvorsen
Elixir and Phoenix are generating tremendous excitement as an unbeatable platform for building modern web application...
New
Hello guys,
I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New







