Swagger based documentation driven development for ExUnit
def deps do
[{:apocryphal, "~> 0.2.0", only: [:test, :dev]}]
endmix deps.get
mix apocryphal.initAdd 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, trueConfigure lib/YOUR_APP/endpoint.ex
if Application.get_env(:YOUR_APP, :sql_sandbox) do
plug Phoenix.Ecto.SQL.Sandbox
endConfigure 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.ymlThen 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
endUnder 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.