Reputation: 8461
It looks like Exunit provides a callback for setup blocks https://hexdocs.pm/ex_unit/ExUnit.Callbacks.html. I'm curious how it can be done with wallaby feature test. I basically walk through creating a user in every scenario when testing integration tests. I basically want to pull out the boilerplate code and use it for every test I write. Here is my current setup for integration test.
test "a user can create a location", %{session: session} do
assert session
|> visit("/users")
|> click_link("Create new account")
|> fill_in("Name", with: "Billy Joel")
|> fill_in("Email", with: "[email protected]")
|> fill_in("Password", with: "password")
|> click_on("Create new user")
|> click_link("New Project")
|> fill_in("Name", with: "Senior Class Air Quality")
|> click_on("Submit")
|> click_link("Senior Class Air Quality")
|> click_link("New Location")
|> click_on("Submit")
|> find(".alert-danger p")
|> text == "Oops, something went wrong! Please check the errors below."
assert session
|> fill_in("Name", with: "A new location")
|> select("Type", option: "office")
|> click_on("Submit")
|> find(".alert-info")
|> text == "Location created successfully."
assert session
|> find("td", count: 3)
|> List.first
|> text == "A new location"
end
test "a user can edit a location", %{session: session} do
project = Factory.project()
Factory.location(%{project_id: project.id})
assert session
|> visit("/users")
|> click_link("Create new account")
|> fill_in("Name", with: "Billy Joel")
|> fill_in("Email", with: "[email protected]")
|> fill_in("Password", with: "password")
|> click_on("Create new user")
|> click_link("New Project")
|> fill_in("Name", with: "Senior Class Air Quality")
|> click_on("Submit")
|> click_link("Senior Class Air Quality")
|> click_link("New Location")
|> fill_in("Name", with: "A new location")
|> select("Type", option: "office")
|> click_on("Submit")
|> click_link("Edit location")
|> fill_in("Name", with: "different name")
|> select("Type", option: "home")
|> click_on("Submit")
|> find(".alert-info")
|> text == "Location updated successfully."
There is a lot of duplication there. How can I use the setup block to consolidate some of this code?
Upvotes: 1
Views: 2209
Reputation: 9018
The setup
and setup_all
functions run the exact same setup code for all the tests in the module and they're the most useful if you need the exact same setup to be performed for all of them. An example of this would be setting up mock services your application depends on.
In your case it seems like you have some of the same steps, but as you add in more tests, there wouldn't necessarily be a common setup for all of them. You can however refactor the individual conceptual steps the tests are composed of into a separate module that you'd use in all the integration tests:
defmodule TestHelpers do
# wallaby imports here
def create_user(session, name, email, password) do
session
|> visit("/users")
|> click_link("Create new account")
|> fill_in("Name", with: name)
|> fill_in("Email", with: email)
|> fill_in("Password", with: password)
|> click_on("Create new user")
end
def create_project(session, name) do
session
|> click_link("New Project")
|> fill_in("Name", with: name)
|> click_on("Submit")
end
def create_location(session, type), do: _modify_location("New Location", session, type, name)
def edit_location(session, type), do: _modify_location("Edit location", session, type, name)
defp _modify_location(link_text, session, location_type, location_name) do
session = session
|> click_link(link_text)
session = case location_name do
nil -> session
x when is_binary(x) -> fill_in(session, "Name", with: location_name)
end
session = case location_type do
nil -> session
x when is_binary(x) -> select(session, "Type", option: location_type)
end
session
|> click_on("Submit")
end
end
Now the tests consist only of the helpers defined in TestHelpers
and the assertions:
defmodule Test do
import TestHelpers
@project_name "Senior Class Air Quality"
# aliases, uses, imports here
test "a user can create a location", %{session: session} do
session = session
|> create_user("Billy Joel", "[email protected]", "password")
|> create_project(@project_name)
|> click_link(@project_name)
|> create_location(nil, nil)
assert session
|> find(".alert-danger p")
|> text == "Oops, something went wrong! Please check the errors below."
session = session
|> create_location("A new location", "office")
assert session
|> find(".alert-info")
|> text == "Location created successfully."
assert session
|> find("td", count: 3)
|> List.first
|> text == "A new location"
end
test "a user can edit a location", %{session: session} do
project = Factory.project()
Factory.location(%{project_id: project.id})
session = session
|> create_user("Billy Joel", "[email protected]", "password")
|> create_project(@project_name)
|> click_link(@project_name)
|> create_location("office", "A new location")
|> modify_location("home", "different_name")
assert session
|> find(".alert-info")
|> text == "Location updated successfully."
end
end
You could also similarly extract the alert messages assertions to turn them into one-liners, which should make each of the tests very readable and robust if, for example, a link text or a div
class changes - you would just make the change in the helpers.
Of course if you find yourself having a repeated multi-step setup in the tests, you can have it as a helper function and use it directly:
def standard_setup(session) do
session
|> create_user("Billy Joel", "[email protected]", "password"
|> create_project(@project_name)
|> click_link(@project_name)
|> create_location("office", "A new location")
end
Upvotes: 1