A BDD framework for your Elixir projects
A BDD framework for your Elixir projects. It's main goal is to provide a rich, expressive syntax for you to develop your unit tests against. Think of it as RSpec's little Elixir-loving brother.
Pavlov is an abstraction built on top of the excellent ExUnit, Elixir's standard testing library, so all of its standard features are still supported.
Here's a short and sweet example of Pavlov in action:
defmodule OrderSpec do use Pavlov.Case, async: true import Pavlov.Syntax.Expectdescribe ".sum" do context "When the Order has items" do let :order do %Order{items: [ {"burger", 10.0} {"fries", 5.2} ]} end
it "sums the prices of its items" do expect Order.sum(order) |> to_eq 15.2 end end
end end
Add Pavlov as a dependency in your
mix.exsfile:
defp deps do [{:pavlov, ">= 0.1.0", only: :test}] end
After you are done, run
mix deps.getin your shell to fetch the dependencies. To start execution of your Pavlov tests, add the following to your 'test/test_helper.exs':
Pavlov.start
Afterwards, running
mix testin your shell will run all test suites.
You may use the
describeand
contextconstructs to group tests together in a logical way. Although
contextis just an alias for
describe, you may use it to add some extra meaning to your tests, ie. you can use
contextswithin a
describedmodule function to simulate different conditions under which your function should work.
You can use
letto define memoized helper methods for your tests. The returning value is cached across all invocations. 'let' is lazily-evaluated, meaning that its body is not evaluated until the first time the method is invoked.
let :order do %Order{items: [ {"burger", 10.0} {"fries", 5.2} ]} end
You can use
subjectto explicitly define the value that is returned by the subject method in the example scope. A subject declared in a context will be available in child contexts as well.
describe "Array" do subject do [1, 2, 3] endit "has the prescribed elements" do assert subject == [1, 2, 3] end
context "Inner context" do it "can use an outer-scope subject" do assert subject == [1, 2, 3] end end end
If you are using Expects syntax together with
subject, you can use the
is_expectedhelper:
describe "Numbers" do subject do: 5context "straight up, no message" do it is_expected |> to_eq 5 end
context "you can also use a custom message" do it "is equal to 5" do is_expected |> to_eq 5 end end end
By default, every created context via
describeor
contextcontains a
subjectthat returns
nil.
You may use the regular ExUnit
assertsyntax if you wish, but Pavlov includes an
expectsyntax that makes your tests more readable.
If you wish to use this syntax, simply import the
Pavlov.Syntax.Expectat the beginning of your Test module:
defmodule MyTest do use Pavlov.Case, async: true import Pavlov.Syntax.Expect #... end
All core matchers are supported under both syntaxes.
When using the
expectssyntax, all matchers have negative counterparts, ie:
elixir expect 1 |> not_to_eq 2 expect(1 > 5) |> not_to_be_true
Visit the Pavlov Wiki to learn more about all of the core matchers available for your tests.
For now, Pavlov only supports callbacks that run before test cases. ExUnit's
on_exitcallback is still fully supported though, and may be used normally inside your
beforecallbacks.
Runs the specified code before every test case.
describe "before :each" do before :each do IO.puts "A test is about to start" :ok endit "does something" do #... end
it "does something else" do #... end end
In this case,
"A test is about to start"is printed twice to the console.
Runs the specified code once before any tests run.
describe "before :all" do before :all do IO.puts "This suite is about to run" :ok endit "does something" do #... end
it "does something else" do #... end end
In this case,
"This suite is about to run"is printed once to the console.
Pavlov provides facilities to mock functions in your Elixir modules. This is achieved using Meck, an erlang mocking tool.
Here's a simple example using HTTPotion:
before :each do allow HTTPotion |> to_receive(get: fn(url) -> "" end) endit "gets a page" do result = HTTPotion.get("http://example.com")
expect HTTPotion |> to_have_received :get expect result |> to_eq "" end
If you want the mock to retain all other functions in the original module, then you will need to pass the
opts
Listargument to the
allowfunction and include the
:passthroughvalue. The
allowfunction specifies a default
opts
Listthat includes the
:no_linkvalue. This value should be included in the
Listas it ensures that the mock (which is linked to the creating process) will unload automatically when a crash occurs.
before :each do allow(HTTPotion, [:no_link, :passthrough]) |> to_receive(get: fn(url) -> "" end) end
Expectations on mocks also work using
assertssyntax via the
calledmatcher:
before :each do allow HTTPotion |> to_receive(get: fn(url) -> "" end) endit "gets a page" do HTTPotion.get("http://example.com")
assert called HTTPotion.get end
You can also perform assertions on what arguments were passed to a mocked method:
before :each do allow HTTPotion |> to_receive(get: fn(url) -> "" end) endit "gets a page" do HTTPotion.get("http://example.com")
expect HTTPotion |> to_have_received :get |> with_args "http://example.com" end
In
assertssyntax:
before :each do allow HTTPotion |> to_receive (get: fn(url) -> url end ) endit "gets a page" do HTTPotion.get("http://example.com")
assert called HTTPotion.get("http://example.com") end
Pavlov runs with the
--exclude pending:trueconfiguration by default, which means that tests tagged with
:pendingwill not be run.
Pavlov offers several convenience methods to skip your tests, BDD style:
Marks a specific test as pending and will not run it.
xit "does not run" do # This will never run end
Marks a group of tests as pending and will not run them. Just as
describeand
context,
xdescribeand
xcontextare analogous.
xdescribe "A pending group" do it "does not run" do # This will never run endit "does not run either" do # This will never run either end end
After cloning the repo, make sure to download all dependencies using
mix deps.get. Pavlov is tested using Pavlov itself, so the general philosophy is to simply write a test using a given feature until it passes.
Simply run
mix test
Run
MIX_ENV=docs mix docs. The resulting HTML files will be output to the
docsfolder.
git checkout -b my-new-feature)
git commit -am 'Add some feature')
git push origin my-new-feature)