title: Phoenix: API integration tests vs controller tests
body: <p>I have an endpoint for signing in users. I wrote both integration tests and controller tests but they look awfully similar, with the exception that I have more edge cases tested in the controller test (e.g. the user tries to log in with an incorrect email). The contents of the test don't really seem to differ, so it would be great if someone could shed some light on how they should be different.</p>
  
  <p>integration test:</p>
  
  <pre><code>defmodule SigningInUserTest do
    use ParrotApi.ConnCase
    alias ParrotApi.Router
  
    @opts Router.init([])
    describe "POST api/v1/sessions" do
      test "success" do
        email = "eric@spaghetti.com"
        password = "ilovemodals"
        user = insert(:user,
                      email: email,
                      password_hash: Comeonin.Bcrypt.hashpwsalt(password))
        user_params = %{
          user: %{
            email: user.email,
            password: password,
          }
        }
  
        conn = build_conn(:post, "/api/v1/sessions", user_params)
        response = Router.call(conn, @opts)
        assert response.status == 201
      end
  
      test "failure" do
        email = "eric@spaghetti.com"
        password = "ilovemodalszzz"
        user = insert(:user,
                      email: email,
                      password_hash: Comeonin.Bcrypt.hashpwsalt(password))
        user_params = %{
          user: %{
            email: user.email,
            password: "bad_password",
          }
        }
  
        conn = build_conn(:post, "/api/v1/sessions", user_params)
        response = Router.call(conn, @opts)
        assert response.status == 401
      end
    end
  </code></pre>
  
  <p>controller test:</p>
  
  <pre><code>defmodule ParrotApi.SessionControllerTest do
    use ParrotApi.ConnCase
  
    setup %{conn: conn} do
      {:ok, conn: put_req_header(conn, "accept", "application/json")}
    end
  
    describe "#create" do
      test "returns the user when the email and password match", %{conn: conn} do
        email = "eric@spaghetti.com"
        password = "ilovemodals"
        user = insert(:user,
                      email: email,
                      password_hash: Comeonin.Bcrypt.hashpwsalt(password))
        rsvp = insert(:rsvp, user: user)
        user_params = %{
          user: %{
            email: email,
            password: password,
          }
        }
        conn = conn
               |&gt; post(session_path(conn, :create), user_params)
        assert json_response(conn, 201)["data"] == %{
          "id" =&gt; user.id,
          "email" =&gt; user.email,
          "name" =&gt; user.name,
          "interests" =&gt; user.interests,
          "location" =&gt; user.location,
          "image_url" =&gt; user.image_url,
          "is_admin" =&gt; user.is_admin,
          "rsvps" =&gt; [rsvp.meetup_id],
        }
      end
  
  
      test "returns an error when the email and password don't match", %{conn: conn} do
        email = "eric@spaghetti.com"
        password = "ilovemodals"
        insert(:user,
               email: email,
               password_hash: Comeonin.Bcrypt.hashpwsalt(password))
        user_params = %{
          user: %{
            email: email,
            password: "bad_password",
          }
        }
        conn = conn
               |&gt; post(session_path(conn, :create), user_params)
        assert json_response(conn, 401) == %{
          "message" =&gt; "The email and password you entered did not match our records. Please try again."
        }
      end
  
      test "returns an error when the user doesn't exist", %{conn: conn} do
        user_params = %{
          user: %{
            email: "eric@spaghetti.com",
            password: "bad_password",
          }
        }
        conn = conn
               |&gt; post(session_path(conn, :create), user_params)
        assert json_response(conn, 401) == %{
          "message" =&gt; "The email and password you entered did not match our records. Please try again."
        }
      end
    end
  end
  </code></pre>