Error messages (expected response with status 422, got: 201) when running tests

The body in the reply here suggests that none of the parameters passed to the create controller action are actually making it to the changeset.

I’d start by checking things:

  • what shape are the parameters to create(conn, params)?

  • what shape are the parameters that Users.create_user expects? What does it actually get when you run the test?

  • what data makes it all the way to User.changeset’s attrs parameter?

@al2o3cr
Here is the user controller

 def create(conn, params) do
    IO.inspect(conn)
    IO.inspect(params)
    with {:ok, %User{} = user} <- Users.create_user(params) do
     IO.inspect(Users.create_user(params))
      conn
      |> put_status(:created)
      |> put_resp_header("location", Routes.user_path(conn, :show, user))
      |> render("show.json", user: user)
    end
  end

IO.inspect(conn)

PS D:\db-service-backend> mix test test\dbservice_web\controllers\user_controller_test.exs
dbservice: generated priv/static/swagger.json
.%Plug.Conn{
  adapter: {Plug.Adapters.Test.Conn, :...},
  assigns: %{},
  body_params: %{
    "user" => %{
      "address" => nil,
      "city" => nil,
      "country" => nil,
      "date_of_birth" => nil,
      "district" => nil,
      "email" => nil,
      "full_name" => nil,
      "gender" => nil,
      "phone" => "nope",
      "pincode" => nil,
      "role" => nil,
      "state" => nil,
      "whatsapp_phone" => nil
    }
  },
  cookies: %{},
  halted: false,
  host: "www.example.com",
  method: "POST",
  owner: #PID<0.458.0>,
  params: %{
    "user" => %{
      "address" => nil,
      "city" => nil,
      "country" => nil,
      "date_of_birth" => nil,
      "district" => nil,
      "email" => nil,
      "full_name" => nil,
      "gender" => nil,
      "phone" => "nope",
      "pincode" => nil,
      "role" => nil,
      "state" => nil,
      "whatsapp_phone" => nil
    }
  },
  path_info: ["api", "user"],
  path_params: %{},
  port: 80,
  private: %{
    DbserviceWeb.Router => {[], %{PhoenixSwagger.Plug.SwaggerUI => []}},
    :before_send => [#Function<0.11807388/1 in Plug.Telemetry.call/2>],
    :phoenix_action => :create,
    :phoenix_controller => DbserviceWeb.UserController,
    :phoenix_endpoint => DbserviceWeb.Endpoint,
    :phoenix_format => "json",
    :phoenix_layout => {DbserviceWeb.LayoutView, :app},
    :phoenix_recycled => true,
    :phoenix_request_logger => {"request_logger", "request_logger"},
    :phoenix_router => DbserviceWeb.Router,
    :phoenix_view => DbserviceWeb.UserView,
    :plug_session_fetch => #Function<1.84243074/1 in Plug.Session.fetch_session/1>,
    :plug_skip_csrf_protection => true
  },
  query_params: %{},
  query_string: "",
  remote_ip: {127, 0, 0, 1},
  req_cookies: %{},
  req_headers: [
    {"accept", "application/json"},
    {"content-type", "multipart/mixed; boundary=plug_conn_test"}
  ],
  request_path: "/api/user",
  resp_body: nil,
  resp_cookies: %{},
  resp_headers: [
    {"cache-control", "max-age=0, private, must-revalidate"},
    {"x-request-id", "F1WHfW2oA8A7DPUAAAPB"}
  ],
  scheme: :http,
  script_name: [],
  secret_key_base: :...,
  state: :unset,
  status: nil
}
%Plug.Conn{
  adapter: {Plug.Adapters.Test.Conn, :...},
  assigns: %{},
  body_params: %{
    "address" => "some address",
    "city" => "some city",
    "country" => "some country",
    "date_of_birth" => ~U[2022-04-28 13:58:00Z],
    "district" => "some district",
    "email" => "some email",
    "full_name" => "some full name",
    "gender" => "some gender",
    "phone" => "9456591269",
    "pincode" => "some pincode",
    "role" => "some role",
    "state" => "some state",
    "whatsapp_phone" => "some whatsapp phone"
  },
  cookies: %{},
  halted: false,
  host: "www.example.com",
  method: "POST",
  owner: #PID<0.462.0>,
  params: %{
    "address" => "some address",
    "city" => "some city",
    "country" => "some country",
    "date_of_birth" => ~U[2022-04-28 13:58:00Z],
    "district" => "some district",
    "email" => "some email",
    "full_name" => "some full name",
    "gender" => "some gender",
    "phone" => "9456591269",
    "pincode" => "some pincode",
    "role" => "some role",
    "state" => "some state",
    "whatsapp_phone" => "some whatsapp phone"
  },
  path_info: ["api", "user"],
  path_params: %{},
  port: 80,
  private: %{
    DbserviceWeb.Router => {[], %{PhoenixSwagger.Plug.SwaggerUI => []}},
    :before_send => [#Function<0.11807388/1 in Plug.Telemetry.call/2>],
    :phoenix_action => :create,
    :phoenix_controller => DbserviceWeb.UserController,
    :phoenix_endpoint => DbserviceWeb.Endpoint,
    :phoenix_format => "json",
    :phoenix_layout => {DbserviceWeb.LayoutView, :app},
    :phoenix_recycled => true,
    :phoenix_request_logger => {"request_logger", "request_logger"},
    :phoenix_router => DbserviceWeb.Router,
    :phoenix_view => DbserviceWeb.UserView,
    :plug_session_fetch => #Function<1.84243074/1 in Plug.Session.fetch_session/1>,
    :plug_skip_csrf_protection => true
  },
  query_params: %{},
  query_string: "",
  remote_ip: {127, 0, 0, 1},
  req_cookies: %{},
  req_headers: [
    {"accept", "application/json"},
    {"content-type", "multipart/mixed; boundary=plug_conn_test"}
  ],
  request_path: "/api/user",
  resp_body: nil,
  resp_cookies: %{},
  resp_headers: [
    {"cache-control", "max-age=0, private, must-revalidate"},
    {"x-request-id", "F1WHfXVHk8Bb4iUAAABj"}
  ],
  scheme: :http,
  script_name: [],
  secret_key_base: :...,
  state: :unset,
  status: nil
}

IO.inspect(params)

PS D:\db-service-backend> mix test test\dbservice_web\controllers\user_controller_test.exs
Compiling 1 file (.ex)
dbservice: generated priv/static/swagger.json
...%{
  "user" => %{
    "address" => nil,
    "city" => nil,
    "country" => nil,
    "date_of_birth" => nil,
    "district" => nil,
    "email" => nil,
    "full_name" => nil,
    "gender" => nil,
    "phone" => "nope",
    "pincode" => nil,
    "role" => nil,
    "state" => nil,
    "whatsapp_phone" => nil
  }
}
%{
  "address" => "some address",
  "city" => "some city",
  "country" => "some country",
  "date_of_birth" => ~U[2022-04-28 13:58:00Z],
  "district" => "some district",
  "email" => "some email",
  "full_name" => "some full name",
  "gender" => "some gender",
  "phone" => "9456591269",
  "pincode" => "some pincode",
  "role" => "some role",
  "state" => "some state",
  "whatsapp_phone" => "some whatsapp phone"
}

IO.inspect(Users.create_user)

{:ok,
 %Dbservice.Users.User{
   __meta__: #Ecto.Schema.Metadata<:loaded, "user">,
   id: 471,
   address: nil,
   city: nil,
   district: nil,
   email: nil,
   full_name: nil,
   gender: nil,
   phone: nil,
   pincode: nil,
   role: nil,
   state: nil,
   whatsapp_phone: nil,
   date_of_birth: nil,
   country: nil,
   inserted_at: ~N[2023-04-13 15:10:19],
   updated_at: ~N[2023-04-13 15:10:19],
   sessions: #Ecto.Association.NotLoaded<association :sessions is not loaded>,
   teacher: #Ecto.Association.NotLoaded<association :teacher is not loaded>,
   student: #Ecto.Association.NotLoaded<association :student is not loaded>,
   group: #Ecto.Association.NotLoaded<association :group is not loaded>
 }}
.{:ok,
 %Dbservice.Users.User{
   __meta__: #Ecto.Schema.Metadata<:loaded, "user">,
   id: 473,
   address: nil,
   city: nil,
   district: nil,
   email: nil,
   full_name: nil,
   gender: nil,
   phone: nil,
   pincode: nil,
   role: nil,
   state: nil,
   whatsapp_phone: nil,
   date_of_birth: nil,
   country: nil,
   inserted_at: ~N[2023-04-13 15:10:19],
   updated_at: ~N[2023-04-13 15:10:19],
   sessions: #Ecto.Association.NotLoaded<association :sessions is not loaded>,
   teacher: #Ecto.Association.NotLoaded<association :teacher is not loaded>,
   student: #Ecto.Association.NotLoaded<association :student is not loaded>,
   group: #Ecto.Association.NotLoaded<association :group is not loaded>
 }}

Assuming this is still the source code of those tests, this is the problem: either create expects its parameters with a user wrapper, or it doesn’t.

The evidence for “wrapper” vs “no wrapper”:

  • “no wrapper”: the passing test for create doesn’t use a wrapper
  • “wrapper”: the phoenix_swagger block says that it should (see my previous comment) about the naming
  • “wrapper”: the wrapper is a common Phoenix idiom because Phoenix forms generate parameters in that shape. Example: def create(conn, %{"user" => params}) do

@al2o3cr Got it , Thanks :+1:

@al2o3cr Hey I still get the same error
I understand Based on the provided code snippets, it appears that the tests are checking for different behavior depending on the data being sent in the POST request. In the first test, the @create_attrs are being sent without a "user" wrapper, while in the second test, the @invalid_attrs are being sent with a "user" wrapper. but this is not working I guess I used something like this %{user: @invalid_attrs})
here is the test code for create and update one update one is successfully ran but create one through error expected 422 but getting 201


  @invalid_attrs %{
    full_name: nil,
    email: nil,
    phone: "nope",
    gender: nil,
    address: nil,
    city: nil,
    country: nil,
    district: nil,
    state: nil,
    pincode: nil,
    role: nil,
    whatsapp_phone: nil,
    date_of_birth: nil
  }

  describe "create user" do
    test "renders user when data is valid", %{conn: conn} do
      conn = post(conn, Routes.user_path(conn, :create), @create_attrs)
      %{"id" => id} = json_response(conn, 201)

      conn = get(conn, Routes.user_path(conn, :show, id))

      assert %{
               "id" => ^id,
               "address" => "some address",
               "city" => "some city",
               "district" => "some district",
               "email" => "some email",
               "full_name" => "some full name",
               "gender" => "some gender",
               "phone" => "9456591269",
               "pincode" => "some pincode",
               "role" => "some role",
               "state" => "some state",
               "whatsapp_phone" => "some whatsapp phone"
             } = json_response(conn, 200)
    end

    test "renders errors when data is invalid", %{conn: conn} do
      conn = post(conn, Routes.user_path(conn, :create), %{user: @invalid_attrs})
      assert json_response(conn, 422)["errors"] != %{}
    end
  end

  describe "update user" do
    setup [:create_user]

    test "renders user when data is valid", %{conn: conn, user: %User{id: id} = user} do
      conn = put(conn, Routes.user_path(conn, :update, user), @update_attrs)
      %{"id" => ^id} = json_response(conn, 200)

      conn = get(conn, Routes.user_path(conn, :show, id))

      assert %{
               "id" => ^id,
               "address" => "some updated address",
               "city" => "some updated city",
               "district" => "some updated district",
               "email" => "some updated email",
               "full_name" => "some updated full name",
               "gender" => "some updated gender",
               "phone" => "9456591269",
               "pincode" => "some updated pincode",
               "role" => "some updated role",
               "state" => "some updated state",
               "whatsapp_phone" => "some updated whatsapp phone"
             } = json_response(conn, 200)
    end

    test "renders errors when data is invalid", %{conn: conn, user: user} do
      conn = put(conn, Routes.user_path(conn, :update, user), @invalid_attrs)
      assert json_response(conn, 422)["errors"] != %{}
    end
  end

yeah now I understand the reason behind the error . The issue has been resolved . Thanks

 def create(conn, %{"user" => user_params}) do
    with {:ok, %User{} = user} <- Users.create_user(user_params) do
      conn
      |> put_status(:created)
      |> put_resp_header("location", Routes.user_path(conn, :show, user))
      |> render("show.json", user: user)
    end
  end

@al2o3cr I have one question here Why are we sending it as a struct?
We never send the parameters as a struct while making any post request.