Proof of concept porting the JS test suite to Elixir.
Currently the basics.js suite has been partially ported over.
To run the suite:
mix deps.get mix test --trace
By default the Elixir tests require CouchDB running at http://127.0.0.1:15984 with credentials adm:pass
. You can override those using the following:
$ EX_USERNAME=myusername EX_PASSWORD=password EX_COUCH_URL=http://my-couchdb.com mix test
X means done, - means partially
Elixir has a number of benefits which makes writing unit tests easier. For example it is trivial to do codegeneration of tests. Below we present a few use cases where code-generation is really helpful.
use Couch.Test.ExUnit.Case
You can run tests either:
make exunit
pwd
ERL_LIBS=pwd
/src MIX_ENV=test mix test --traceSometimes we have some data in structured format and want to generate test cases using that data. This is easy in Elixir. For example suppose we have following spec:
{ "{db_name}/_view_cleanup": { "roles": ["_admin"] } }
We can use this spec to generate test cases
defmodule GenerateTestsFromSpec do use ExUnit.Case require Record Record.defrecordp :user_ctx, Record.extract(:user_ctx, from_lib: "couch/include/couch_db.hrl") Record.defrecordp :httpd, Record.extract(:httpd, from_lib: "couch/include/couch_db.hrl") {:ok, spec_bin} = File.read("roles.json") spec = :jiffy.decode(spec_bin, [:return_maps]) Enum.each spec, fn {path, path_spec} -> roles = path_spec["roles"] @roles roles @path_parts String.split(path, "/") test "Access with `#{inspect(roles)}` roles" do req = httpd(path_parts: @path_parts, user_ctx: user_ctx(roles: @roles)) :chttpd_auth_request.authorize_request(req) end end end
As a result we would get
GenerateTestsFromSpec * test Access with `["_admin"]` roles (0.00ms)
Sometimes we want to test all possible permutations for parameters. This can be accomplished using something like the following:
defmodule Permutations do use ExUnit.Case pairs = :couch_tests_combinatorics.product([ [:remote, :local], [:remote, :local] ]) for [source, dest] <- pairs do @source source @dest dest test "Replication #{source} -> #{dest}" do assert :ok == :ok end end end
This would produce following tests
Permutations * test Replication remote -> remote (0.00ms) * test Replication local -> remote (0.00ms) * test Replication remote -> local (0.00ms) * test Replication local -> local (0.00ms)
The setup functions are quite similar in lots of tests therefore it makes sense to reuse them. The idea is to add shared setup functions into either
The setup functions looks like the following:
defmodule Foo do alias Couch.Test.Setup.Step def httpd_with_admin(setup) do setup |> Step.Start.new(:start, extra_apps: [:chttpd]) |> Step.User.new(:admin, roles: [:server_admin]) end end
These parts of a setup chain can be invoked as follows:
defmodule Couch.Test.CRUD do use Couch.Test.ExUnit.Case alias Couch.Test.Utils alias Couch.Test.Setup alias Couch.Test.Setup.Step def with_db(context, setup) do setup = setup |> Setup.Common.httpd_with_db() |> Setup.run() context = Map.merge(context, %{ db_name: setup |> Setup.get(:db) |> Step.Create.DB.name(), base_url: setup |> Setup.get(:start) |> Step.Start.clustered_url(), user: setup |> Setup.get(:admin) |> Step.User.name() }) {context, setup} end describe "Database CRUD using Fabric API" do @describetag setup: &__MODULE__.with_db/2 test "Create DB", ctx do IO.puts("base_url: #{ctx.base_url}") IO.puts("admin: #{ctx.user}") IO.puts("db_name: #{ctx.db_name}") end end end