Other articles in the series
The backend related code for this post is available here.
The front-end related code for this post is available at here.
In this post, we will fix the failing tests. Let’s take a look at a failing test and see how to fix it. Maybe it can also be a bug in our code.
mix test test/ms/order_management_test.exs
Consider the below error message.
test order_items update_order_item/2 with invalid data returns error changeset (Ms.OrderManagementTest)
test/ms/order_management_test.exs:126
** (MatchError) no match of right hand side value: {:error, #Ecto.Changeset<action: :insert, changes: %{amount: 42, order_id: 1973, unit_price: 120.5}, errors: [product_id: {"can't be blank", [validation: :required]}], data: #Ms.OrderManagement.OrderItem<>, valid?: false>}
code: order_item = order_item_fixture()
stacktrace:
test/ms/order_management_test.exs:89: Ms.OrderManagementTest.order_item_fixture/1
test/ms/order_management_test.exs:127: (test)
As you can see test complains about missing product_id which is required. This is because our order item requires a reference to a product. In order to fix this lets add a function to build a product, so that its product id can be passed to the order item. So the product fixture in test/ms/order_management_test.exs is as follows.
@valid_attrs_product %{name: "some name", price: 120.5, stock: 42, tax: 120.5}
def product_fixture(attrs \\ %{}) do
{:ok, product} =
attrs
|> Enum.into(@valid_attrs_product)
|> InventoryManagement.create_product()
product
end
Since we use create_product from InventoryManagement, we need to alias alias Ms.InventoryManagement at the top of the file.
And now in to our order_item_fixture function add product_id as follows.
def order_item_fixture(attrs \\ %{}) do
order = order_fixture()
product = product_fixture()
valid_attrs = Map.merge(@valid_attrs, %{"order_id" => order.id, "product_id" => product.id})
{:ok, order_item} =
attrs
|> Enum.into(valid_attrs)
|> OrderManagement.create_order_item()
order_item
end
This should fix 6 out of 7 failures, leaving create_order_item with valid data as the only failing one. As you notice the error is the same like before. Fix is similar to previous fixes. Take a look at code to see exactly how.
Now lets take a look at failing tests in test/ms_web/controllers/order_item_controller_test.exs.
mix test test/ms_web/controllers/order_item_controller_test.exs
The partial result is as follows.
test create order_item renders order_item when data is valid (MsWeb.OrderItemControllerTest)
test/ms_web/controllers/order_item_controller_test.exs:59
** (RuntimeError) expected response with status 201, got: 422, with body:
{"errors":{"product_id":["can't be blank"]}}
code: assert %{"id" => id} = json_response(conn, 201)["data"]
stacktrace:
(phoenix) lib/phoenix/test/conn_test.ex:373: Phoenix.ConnTest.response/2
(phoenix) lib/phoenix/test/conn_test.ex:419: Phoenix.ConnTest.json_response/2
test/ms_web/controllers/order_item_controller_test.exs:63: (test)
The error is similar to previous case, where we lack product_id. Here too we add a function to create a product and an order. See the code below.
defp create_order(_) do
order= order_fixture()
{:ok, order: order}
end
defp create_product(_) do
product = product_fixture()
{:ok, product: product}
end
@create_attrs_product %{
name: "some name",
price: 120.5,
stock: 42,
tax: 120.5
}
def product_fixture() do
{:ok, product} = InventoryManagement.create_product(@create_attrs_product)
product
end
Also make sure we add alias Ms.InventoryManagement to the top of the file, as we use order_fixture and create_product from Ms.InventoryManagement.
Now we just change the test as follows.
describe "create order_item" do
setup [:create_product, :create_order]
test "renders order_item when data is valid", %{conn: conn, product: product, order: order} do
create_attrs = Map.merge(@create_attrs_order_item, %{"order_id": order.id, "product_id": product.id})
conn = post(conn, Routes.order_item_path(conn, :create), order_item: create_attrs)
assert %{"id" => id} = json_response(conn, 201)["data"]
conn = get(conn, Routes.order_item_path(conn, :show, id))
assert %{
"id" => id,
"amount" => 42,
"unit_price" => 120.5
} = json_response(conn, 200)["data"]
end
Here the setup [:create_product, :create_order] line says that the result of create_product and create_order is passed as input to the test. In this case it is %{conn: conn, product: product, order: order}. The conn: conn is automatically injected.
After applying a bit more polish, like renaming attributes to more meaningful names and fixing all compile errors, all our tests will pass. As the code changes are pretty trivial please take a look at the commit.
That’s all folks for this part. In next part, we will see how to deploy our app to the real world. Stay tuned :)