Unit testing phoenix with put does not change map


I’m trying to do a unit test with ex_machina like so

    test "renders errors when data is invalid", %{conn: conn,  group: group, shipment: shipment, route: route} do
      conn = put conn, group_shipment_route_path(conn, :update, group, shipment, route), route: params_for(:invalid_route)
      assert html_response(conn, 200) =~ "Edit Route"

where the value I want to place in looks like this

  def invalid_route_factory do
        label: nil


my changeset and schema looking like this

  schema "routes" do
    field :label, :string, null: false
    field :address, :string
    field :date, :string
    field :groups, :string
    field :checklist, {:array, :string}
    belongs_to :shipment, Shipment # on_delete set in database via migration


  @doc false
  def changeset(route, attrs) do
    |> cast(attrs, [:address, :date, :groups, :label, :shipment_id, :checklist])
    |> validate_required([:label, :shipment_id])

and the function I am trying to test looking like this

  def update(conn, %{"route" => route_params} = params) do
    { group_id, shipment_id, route_id } = get_ids(params)
    route = Shipments.get_route!(route_id)
    case Shipments.update_route(route, route_params) do
      {:ok, route} ->
        |> put_flash(:info, "Route updated successfully.")
        |> redirect(to: group_shipment_route_path(conn, :show, group_id, shipment_id, route))
      {:error, %Ecto.Changeset{} = changeset} ->
        render(conn, "edit.html", group: group_id, shipment: shipment_id, route: route, changeset: changeset)

the error I get indicates that the case returns {:ok, route} rather than :error, which should not be the case since :label should be validated against null as you can see in my schema.

This is the error I get that indicates I get {:ok, route}, and in my conn struct I see the route wasn’t created from my ex_machina factory.

  1) test update route renders errors when data is invalid (FerryWeb.RouteControllerTest)
     ** (RuntimeError) expected response with status 200, got: 302, with body:
     <html><body>You are being <a href="/groups/74/shipments/183/routes/120">redirected</a>.</body></html>
     code: assert html_response(conn, 200) =~ "Edit Route"
       (phoenix) lib/phoenix/test/conn_test.ex:362: Phoenix.ConnTest.response/2
       (phoenix) lib/phoenix/test/conn_test.ex:376: Phoenix.ConnTest.html_response/2
       test/ferry_web/controllers/route_controller_test.exs:75: (test)

Is there anyway I can debug why this is happening?

This is just an issue with your test case actually.

What this is saying is that the HTTP PUT request you made in your test case got a 302 response. The 302 response is the redirection response code. If you look at your update controller function, this is exactly what you tell it to do:

When you hit the update function from your browser, it gets the 302 status code and immediately redirects to the show page, which can make it less obvious that two independent HTTP requests happen. However in your test case there isn’t an automatic redirect follow that happens.


@benwilson512 The test case is (trying to) test the error block of the code. That’s why it’s looking for a 200 status (i.e. the render call when it hits the error case).

Having said that, it is an issue with the test case because this works in the actual application. I’m just having trouble figuring out why the put call doesn’t change my route struct to be label:nil

What are your actual params?

I figured it out, the issue was with the ex_machina library. If you put something as “nil”, it will not include it in the struct, which is why I couldn’t get my test to go down the “error” branch of my code

1 Like