ExUnit: Assertion with "in" failed

In chapter 8 of Programming Phoenix, the user_test.exs failed, located in testing_mvc/listings/rumbl/test/models/user_test.exs, and it’s not clear what the problem is. (context)

    test changeset does not accept long usernames (Rumbl.UserTest)
         Assertion with in failed
         code: {:username, {"should be at most %{count} character(s)", [count: 20]}} in errors_on(%User{}, attrs)
         lhs:  {:username, {"should be at most %{count} character(s)", [count: 20]}}
         rhs:  [username: "should be at most 20 character(s)"]
           test/models/user_test.exs:28: (test)

Can someone please help with the debugging thought process for the following error message? When manually checking the UI for the character limit, my changeset/2 function in web/model/user.ex correctly handles the username constraint. Here’s that function (the only difference with the original from the book is that I set the default params value to an empty map instead of an :empty atom.

  # see https://github.com/elixir-ecto/ecto/blob/master/CHANGELOG.md
  # def changeset(model, params \\ :empty) do
  def changeset(model, params \\ %{}) do
    |> cast(params, ~w(name username), [])
    |> validate_length(:username, min: 1, max: 20)
    |> unique_constraint(:username)
1 Like

{:username, {""should be at most %{count} ...", ...} is how the errors are represented in the your_changeset.errors keyword list.

errors_on/2, which is a Phoenix test helper, transforms the errors into another keyword list by using Ecto.Changeset.traverse_errors/2 - what you get back is e.g.: [username: "should be at most 20 character(s)"].

Thus, in your test you need to either do:

  1. assert {:username, {"should be at most %{count} ...", ...}} in changeset.errors
  2. assert {:username, "should be at most 20 character(s)"} in errors_on(...)
1 Like

The most recent version of errors_on/2 looks like this:

def errors_on(struct, data) do
    struct.__struct__.changeset(struct, data)
    |> Ecto.Changeset.traverse_errors(&<%= application_module %>.ErrorHelpers.translate_error/1)
    |> Enum.flat_map(fn {key, errors} -> for msg <- errors, do: {key, msg} end)

However anybody working through Programming Phoenix has been advised to use phoenix version 1.1.6:[quote=“josevalim, post:2, topic:1107”]
Stick with phoenix_new 1.1.6 for the book. The changes are few but it will help avoid confusion in case you get stuck somewhere. Migrating to 1.2.0 should be straight-forward later on.
Looking into rumbl/test/support/model_case.ex one finds:

  def errors_on(model, data) do
    model.__struct__.changeset(model, data).errors

Here are my mix.exs dependencies that didn’t cause any problems during the chapter 8 test cases:

  defp deps do
    [{:phoenix, "~> 1.1.6"},
     {:postgrex, ">= 0.0.0"},
     {:phoenix_ecto, "~> 2.0"},
     {:phoenix_html, "~> 2.4"},
     {:phoenix_live_reload, "~> 1.0", only: :dev},
     {:gettext, "~> 0.9"},
     {:cowboy, "~> 1.0"},
     {:comeonin, "~> 2.0"},
     {:sweet_xml, "~> 0.5.0"}]
1 Like


I’m also reading the book and got stuck at the same point. Weirdly the rest of the tests go off changeset.errors. So using the password test in the book and adjusting so it matches the username query got it working for me. Here is the code for the username test.

  test "username must be at least 20 chars long" do
    attrs = Map.put(@valid_attrs, :username, "superlongusernamepast20")
    changeset = User.registration_changeset(%User{}, attrs)
    assert {:username, {"should be at most %{count} character(s)", count: 20}} in changeset.errors

Got to this one myself just now. This is what worked for me:

test "changeset does not accept long usernames" do
  attrs = Map.put(@valid_attrs, :username, String.duplicate("a", 30))
  changeset = User.registration_changeset(%User{}, attrs)
  assert [ username: {"should be at most %{count} character(s)", [count: 30, validation: :length, max: 20]} ] in changeset.errors