mix.exs
file to add ExMachina to the deps
and application
lists. To do this, simply insert another entry for ExMachina into the list of dependencies immediately after ComeOnIn:
defp deps do [{:phoenix, "~> 1.2.0"}, {:phoenix_pubsub, "~> 1.0"}, {:phoenix_ecto, "~> 3.0"}, {:postgrex, ">= 0.0.0"}, {:phoenix_html, "~> 2.6"}, {:phoenix_live_reload, "~> 1.0", only: :dev}, {:gettext, "~> 0.11"}, {:cowboy, "~> 1.0"}, {:comeonin, "~> 2.5.2"}, {:ex_machina, "~> 1.0"}] end
:ex_machina
to the list of used applications:
def application do [mod: {Pxblog, []}, applications: [:phoenix, :phoenix_pubsub, :phoenix_html, :cowboy, :logger, :gettext, :phoenix_ecto, :postgrex, :comeonin, :ex_machina]] end
$ mix do deps.get, compile
mix test
and make sure that all tests are green.
test/support
directory and then write its import in the tests we need.
test/support/factory.ex
file:
defmodule Pxblog.Factory do use ExMachina.Ecto, repo: Pxblog.Repo alias Pxblog.Role alias Pxblog.User alias Pxblog.Post def role_factory do %Role{ name: sequence(:name, &"Test Role #{&1}"), admin: false } end end
Factory
because such a name reflects the essence of this module. Then we will use special factory functions. They compare with the sample an atom supplied to the input, which determines which type of factory to assemble / create . Since this library is pretty close to Factory Girl, it also brings with it some naming conventions that are important to know. The first such name will be build
. The build
function means that the model ( not the revision ) will be built without saving to the database. The second agreement will be the name of the insert
function, which vice versa saves the model in the database, thereby creating it.
use ExMachina.Ecto
so that ExMachina uses Ecto as the Repo layer and behaves accordingly when creating models, associations, etc. We also need to add pseudonyms to all the models for which we will write factories.
role_factory
function should simply return a Role
structure that defines default properties. This feature only supports arity 1.
sequence
function is pretty curious. We need to generate a unique name for each role. Therefore, we will make it sequentially generated. To do this, we take the function sequence
, in which we pass two arguments: the first is the name of the field for which we want to generate a sequence, the second is an anonymous function, which returns a string and interpolates the value inside it. Let's take a look at this feature:
&”Test Role #{&1}”
fn x -> "Test Role #{x}" end
sequence
function can be explained this way:
sequence(:name, fn x -> "Test Role #{x}" end)
false
, because we use this value as the default condition. We can create the administrative role by specifying this explicitly. Other more advanced features of ExMachina let's discuss a little later. Now we will spend some time combining our new factory Role
with controller tests.
test/controllers/user_controller_test.exs
. At the top, in the setup
block, add the use of our new function TestHelper.create_role
:
# ... import Pxblog.Factory @valid_create_attrs %{email: "test@test.com", username: "test", password: "test", password_confirmation: "test"} @valid_attrs %{email: "test@test.com", username: "test"} @invalid_attrs %{} setup do user_role = insert(:role) {:ok, nonadmin_user} = TestHelper.create_user(user_role, %{email: "nonadmin@test.com", username: "nonadmin", password: "test", password_confirmation: "test"}) admin_role = insert(:role, admin: true) {:ok, admin_user} = TestHelper.create_user(admin_role, %{email: "admin@test.com", username: "admin", password: "test", password_confirmation: "test"}) {:ok, conn: build_conn(), admin_role: admin_role, user_role: user_role, nonadmin_user: nonadmin_user, admin_user: admin_user} end # ...
:role
factory. In line 13, we do the same, but override the admin flag to true
.
def user_factory do %User{ username: sequence(:username, &"User #{&1}"), email: "test@test.com", password: "test1234", password_confirmation: "test1234", password_digest: Comeonin.Bcrypt.hashpwsalt("test1234"), role: build(:role) } end
password_digest
value to the password password
hash value (as we simulate user login, we need to add this as well). We simply call the Bcrypt module from Comeonin and use the hashpwsalt
function, passing the same value to it as in the password
/ password_confirmation
fields. On the next line, we also set role
as an association. We use the build
function and pass in it the name of the association we want to build, in the form of an atom.
test/controllers/user_controller_test.exs
.
setup do user_role = insert(:role) nonadmin_user = insert(:user, role: user_role) admin_role = insert(:role, admin: true) admin_user = insert(:user, role: admin_role) {:ok, conn: build_conn(), admin_role: admin_role, user_role: user_role, nonadmin_user: nonadmin_user, admin_user: admin_user} end
TestHelper
calls to the factory. We take the role and transfer it to the factory to create a user with the right role. Then, do the same with the administrator, but we don’t need to change our tests!
test/controllers/post_controller_test.exs
:
def post_factory do %Post{ title: "Some Post", body: "And the body of some post", user: build(:user) } end
import
the Pxblog.Factory
module Pxblog.Factory
that our tests know where the factory is to which we send calls. Then we replace all the steps to create a post in the setup
block with a factory call. Using the insert
function, a role
structure is created, which is then used to create a user through the factory, which is finally used to create the post associated with it ... Just something!
TestHelper
with Factory
calls. This is not particularly new or exciting, so I will not pay undue attention to explaining the details.
using
block in the test/support/model_case.ex
:
using do quote do alias Pxblog.Repo import Ecto import Ecto.Changeset import Ecto.Query import Pxblog.ModelCase import Pxblog.Factory end end
test/support/conn_case.ex
:
using do quote do # Import conveniences for testing with connections use Phoenix.ConnTest alias Pxblog.Repo import Ecto import Ecto.Changeset import Ecto.Query import Pxblog.Router.Helpers import Pxblog.Factory # The default endpoint for testing @endpoint Pxblog.Endpoint end end
build
and create
there is support for some other functions for the sake of convenience (I use build
as an example, but it also works with create
):
build_pair(:factory, attrs) <- Builds 2 models build_list(n, :factory, attrs) <- Builds N models
build
method by calling create
on it:
build(:role) |> insert
Source: https://habr.com/ru/post/316996/