Created
January 31, 2019 21:09
-
-
Save ibarchenkov/b5982e42e660fe33eb023c164b1424d3 to your computer and use it in GitHub Desktop.
Elixir Ecto testing factories combined with StreamData generators.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule MyApp.Factory do | |
use ExUnitProperties | |
alias MyApp.{Repo, User, Comment} | |
### Generators | |
def generator(:user) do | |
gen all name <- string(:alphanumeric, min_length: 2), | |
email <- generator(:email), | |
age <- integer(10..130) do | |
%User{name: name, email: email, age: age} | |
end | |
end | |
def generator(:comment) do | |
gen all body <- string(:alphanumeric, min_length: 10), | |
user <- generator(:user) do | |
%Comment{body: body, user: user} | |
end | |
end | |
def generator(:user_with_comments) do | |
gen all user <- generator(:user), | |
comments <- | |
list_of( | |
# modifying the comment generator to return a comment without a user to prevent Ecto association error | |
bind(generator(:comment), fn comment -> | |
constant(%{comment | user: nil}) | |
end), | |
length: 1..3 | |
) do | |
%{user | comments: comments} | |
end | |
end | |
## helper generators | |
def generator(:email) do | |
gen all local <- string(:alphanumeric, min_length: 1), | |
domain <- string(:alphanumeric, min_length: 1) do | |
local <> to_string(System.unique_integer()) <> "@" <> domain | |
end | |
end | |
### Factory API | |
def build(generator_name, attributes \\ []) do | |
1 |> build_list(generator_name, attributes) |> List.first() | |
end | |
def build_list(count, generator_name, attributes \\ []) do | |
generator_name | |
|> apply_attrs_to_generator(attributes) | |
|> Enum.take(count) | |
end | |
def insert(generator_name, attributes \\ []) do | |
1 |> insert_list(generator_name, attributes) |> List.first() | |
end | |
def insert_list(count, generator_name, attributes \\ []) do | |
generator_name | |
|> apply_attrs_to_generator(attributes) | |
|> Stream.map(&Repo.insert!/1) | |
|> Enum.take(count) | |
end | |
defp apply_attrs_to_generator(generator_name, attributes) do | |
generator_name | |
|> generator | |
|> Stream.map(&struct(&1, attributes)) | |
end | |
end | |
defmodule MyApp.FactoryTest do | |
use MyApp.DataCase | |
use ExUnitProperties | |
import MyApp.Factory # better to import it in MyApp.DataCase | |
property "generate a user with comments" do | |
check all user <- generator(:user_with_comments) do | |
assert length(user.comments) >= 1 | |
# do something with %User{} | |
end | |
end | |
describe "generators can be used as factories in example-based tests" do | |
test "build/2" do | |
assert comment = %MyApp.Comment{} = build(:comment) | |
assert comment.user | |
end | |
test "build_list/3" do | |
users = build_list(3, :user, name: "Andrea") | |
assert Enum.all?(users, &(&1.name == "Andrea")) | |
end | |
test "insert/2" do | |
user = insert(:user, name: "Michał") | |
comment = insert(:comment, user: user) | |
assert comment.id | |
assert comment.user == user | |
end | |
test "insert_list/3" do | |
users = insert_list(5, :user_with_comments) | |
assert users == Enum.uniq_by(users, fn user -> user.email end) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment