Awesome
Apocryphal
Swagger based documentation driven development for ExUnit
Installation
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 for Phoenix (HTTP Requests, SQL Sandbox)
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
}
Configure for Plug
Usage
Generating an API verification test
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
.
Full Example
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
Using remote swagger files
Under the hood Apocryphal uses ExJsonSchema. To set up resolves for remote schemas see the ExJsonSchema docs
Note for Umbrella apps
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.