zoedsoupe
How do you implement integration tests for a Phoenix API?
That question maybe look as too generic but I really want to have an initial idea about how do you make integration/acceptance tests on a Phoenix API.
Some point are:
- do you use any libraries or builtins function in
ConnCase/ChannelCaseare enough? - any suggestions with extra content and material about this topic and phoenix/elixir?
- could you provide a simple example test that can’t be covered with Phoenix’s builtin functions
Marked As Solved
stefanchrobot
I write the integration tests in ExUnit which has the benefit of the tests being ran as part of the normal test suite. I abstract away any Phoenix helpers since they are taking some shortcuts. I follow the approach of Hex: we should be testing behaviours, not the implementation, but the unit of behaviour here is an API endpoint - it’s the public contract. I try to avoid shared setups as much as possible - this way the tests are self-contained. I invest into building helpers to combat code duplication. UserUI is a module with helpers that mimic user actions in the web app (creating account, sending a message, etc.). MobileApi is a module with helpers that basically do HTTP requests but are tailored to this specific API (API-key based auth with JSON). Example test with test helpers:
defmodule MobileApiTest do
use MyApp.Case, async: true
alias MyApp.TestHelpers.UserUI
alias MyApp.TestHelpers.MobileApi
describe "GET /api/mobile/messages" do
test "lists messages sent to the person" do
# Setup...
account = UserUI.create_account!()
message1 = UserUI.create_message!(account)
# More setup...
assert MobileApi.get("/api/mobile/messages", api_key) == %{
status: 200,
body: %{
"messages" => [
%{
"id" => message2.id,
"subject" => message2.subject,
"sent_at" => DateTime.to_iso8601(message2.inserted_at)
},
%{
"id" => message1.id,
"subject" => message1.subject,
"sent_at" => DateTime.to_iso8601(message1.inserted_at)
}
]
}
}
end
test "returns 401 on expired API key" do
# Setup...
assert %{
status: 401,
body: %{"error" => "expired_api_key"}
} = MobileApi.get("/api/mobile/messages", api_key)
end
end
end
defmodule MyApp.TestHelpers.MobileApi do
@moduledoc """
Test helpers for interacting with the mobile app API.
"""
alias Plug.Conn
alias Phoenix.ConnTest
require Phoenix.ConnTest
# The default endpoint for testing.
@endpoint MyApp.Endpoint
def get(url, api_key \\ nil) do
%{status: status, resp_body: body} =
api_key
|> build_conn()
|> ConnTest.get(url)
%{status: status, body: if(body != "", do: Jason.decode!(body), else: "")}
end
defp build_conn(api_key) do
conn =
ConnTest.build_conn()
|> Conn.put_req_header("accept", "application/json")
if api_key do
conn |> Conn.put_req_header("authorization", "Bearer #{api_key}")
else
conn
end
end
end
Last Post!
karlosmid
Hi!
Using code coverage: mix test --cover be sure to have:
- source code statement coverage
- combinations for branch (if then else) coverage
- do not forget test data
Popular in Discussions
Other popular topics
Latest Phoenix Threads
Chat & Discussions>Discussions
Latest on Elixir Forum
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex









