Swagger based documentation driven development for ExUnit
def deps do
[{:apocryphal, "~> 0.2.0", only: [:test, :dev]}]
end
mix deps.get
mix apocryphal.init
Add HTTPoison.start
to test_helper.exs:
ExUnit.start
HTTPoison.start
Ecto.Adapters.SQL.Sandbox.mode(PetStore.Repo, :manual)
Configure test.exs
env to use Ecto SQL Sandbox and serve content
config :YOUR_APP, YOUR_APP.Endpoint,
http: [port: 4001],
server: true
config :YOUR_APP, :sql_sandbox, true
Configure lib/YOUR_APP/endpoint.ex
if Application.get_env(:YOUR_APP, :sql_sandbox) do
plug Phoenix.Ecto.SQL.Sandbox
end
Configure Apocryphal
config :apocryphal,
port: 4001,
host: "localhost",
serializers: %{
"application/json" => fn(body) -> Poison.encode!(body) end
},
deserializers: %{
"application/json" => fn(body) -> Poison.decode!(body) end
}
Parse swagger documentation into ExUnit tests
mix apocryphal.gen.test V1.Pets --only=^\/pets --swagger-file=./docs/pet_store.yml
mix apocryphal.gen.test V1.Stores --only=^\/stores --swagger-file=./docs/pet_store.yml
"One big file" mode:
mix apocryphal.gen.test V1.PetAPI --swagger-file=./docs/pet_store.yml
Then just run mix test
.
Check out the Petz Sample Phoenix app
defmodule PetStore.V1.PetAPITest do
use Apocryphal.Case
alias PetStore.Pet
alias PetStore.Store
@swagger "./docs/pet_store.yml"
@mime "application/json"
test "[GET] /stores (200)" do
%Store{ address: "123 Ship St.",
city: "Los Angeles",
state: "CA",
postal_code: "90210" } |> Repo.insert!
# assert_schema/1 will dispatch the transaction if a request isn't
# present, or it can be manually dispatched
@swagger
|> Apocryphal.Transaction.get("/stores", 200, @mime)
|> Apocryphal.Transaction.dispatch
|> assert_schema
# @swagger
# |> Apocryphal.Transaction.get("/stores", 200, @mime)
# |> assert_schema
end
test "[GET] /pets (200)" do
%Pet{ name: "Chauncy", type: "dog" } |> Repo.insert!
@swagger
|> Apocryphal.Transaction.get("/pets", 200, @mime)
|> put_in([:request, :params], [limit: 20])
|> assert_schema
end
test "[POST] /pets (201)" do
pet_params = %{ pet: %{ name: "Chuancy", type: "cat" } }
@swagger
|> Apocryphal.Transaction.post("/pets", 201, @mime)
|> put_in([:request, :body], pet_params)
|> assert_schema
end
test "[POST] /pets 422" do
pet_params = %{ pet: %{ name: "Doge", type: "pupperino" } }
@swagger
|> Apocryphal.Transaction.post("/pets", 422, @mime)
|> put_in([:request, :body], pet_params)
|> assert_schema
end
test "[GET] /pets/{id} (200)" do
pet = %Pet{name: "Chauncy", type: "cat"} |> Repo.insert!
@swagger
|> Apocryphal.Transaction.get("/pets/{id}", 200, @mime)
|> put_in([:request, :path_params], %{"id" => pet.id})
|> assert_schema
end
test "[GET] /pets/{id} (404)" do
@swagger
|> Apocryphal.Transaction.get("/pets/{id}", 404, @mime)
|> put_in([:request, :path_params], %{"id" => "-1"})
|> assert_schema
end
end
Under the hood Apocryphal uses ExJsonSchema. To set up resolves for remote schemas see the ExJsonSchema docs
If you are creating an umbrella app YOUR_APP
above should be the app containing the ecto repo!
Issues have been experience with async tests with umbrella apps where Ecto raises an Ownership.Error
. Setting the tests explicitly as async: false
fixes this.