{"id":13509237,"url":"https://github.com/sproutapp/pavlov","last_synced_at":"2026-02-22T10:34:08.081Z","repository":{"id":62430276,"uuid":"27221358","full_name":"sproutapp/pavlov","owner":"sproutapp","description":"A BDD framework for your Elixir projects","archived":false,"fork":false,"pushed_at":"2016-02-04T15:17:43.000Z","size":106,"stargazers_count":129,"open_issues_count":10,"forks_count":7,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-10-06T02:41:08.315Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sproutapp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-11-27T10:57:03.000Z","updated_at":"2024-09-22T22:27:26.000Z","dependencies_parsed_at":"2022-11-01T20:21:03.202Z","dependency_job_id":null,"html_url":"https://github.com/sproutapp/pavlov","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sproutapp%2Fpavlov","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sproutapp%2Fpavlov/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sproutapp%2Fpavlov/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sproutapp%2Fpavlov/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sproutapp","download_url":"https://codeload.github.com/sproutapp/pavlov/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":222552787,"owners_count":17002157,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T02:01:04.979Z","updated_at":"2025-10-21T16:05:30.197Z","avatar_url":"https://github.com/sproutapp.png","language":"Elixir","funding_links":[],"categories":["Testing"],"sub_categories":[],"readme":"# Pavlov\n[![Build Status](https://travis-ci.org/sproutapp/pavlov.svg?branch=master)](https://travis-ci.org/sproutapp/pavlov)\n[![Inline docs](http://inch-ci.org/github/sproutapp/pavlov.svg?branch=master\u0026style=flat)](http://inch-ci.org/github/sproutapp/pavlov)\n\nA 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.\n\nPavlov is an abstraction built on top of the excellent ExUnit, Elixir's standard testing library, so all of its standard features are still supported.\n\nHere's a short and sweet example of Pavlov in action:\n\n```elixir\ndefmodule OrderSpec do\n  use Pavlov.Case, async: true\n  import Pavlov.Syntax.Expect\n\n  describe \".sum\" do\n    context \"When the Order has items\" do\n      let :order do\n        %Order{items: [\n          {\"burger\", 10.0}\n          {\"fries\", 5.2}\n        ]}\n      end\n\n      it \"sums the prices of its items\" do\n        expect Order.sum(order) |\u003e to_eq 15.2\n      end\n    end\n  end\nend\n```\n\n## Table Of Contents\n\n- [Usage](#usage)\n- [Describe and Context](#describe-and-context)\n- [Let](#let)\n- [Subject](#subject)\n- [Expects syntax](#expects-syntax)\n- [Included Matchers](#included-matchers)\n- [Callbacks](#callbacks)\n  - [before(:each)](#beforeeach)\n  - [before(:all)](#beforeall)\n- [Mocking](#mocking)\n\t- [Mocks with arguments](#mocks-with-arguments)\n- [Skipping tests](#skipping-tests)\n\t- [xit](#xit)\n\t- [xdescribe/xcontext](#xdescribexcontext)\n- [Development](#development)\n\t- [Running the tests](#running-the-tests)\n\t- [Building the docs](#building-the-docs)\n- [Contributing](#contributing)\n\n## Usage\nAdd Pavlov as a dependency in your `mix.exs` file:\n\n```elixir\ndefp deps do\n  [{:pavlov, \"\u003e= 0.1.0\", only: :test}]\nend\n```\n\nAfter you are done, run `mix deps.get` in your shell to fetch the dependencies.\nTo start execution of your Pavlov tests, add the following to your 'test/test_helper.exs':\n\n```elixir\nPavlov.start\n```\n\nAfterwards, running `mix test` in your shell will run all test suites.\n\n## Describe and Context\nYou may use the `describe` and `context` constructs to group tests together in a logical way. Although `context` is just an alias for `describe`, you may use it to add some extra meaning to your tests, ie. you can use `contexts` within a `described` module function to simulate different conditions under which your function should work.\n\n## Let\nYou can use `let` to 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.\n\n```elixir\nlet :order do\n  %Order{items: [\n    {\"burger\", 10.0}\n    {\"fries\", 5.2}\n  ]}\nend\n```\n\n## Subject\nYou can use `subject` to 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.\n\n```elixir\ndescribe \"Array\" do\n  subject do\n    [1, 2, 3]\n  end\n\n  it \"has the prescribed elements\" do\n    assert subject == [1, 2, 3]\n  end\n\n  context \"Inner context\" do\n    it \"can use an outer-scope subject\" do\n      assert subject == [1, 2, 3]\n    end\n  end\nend\n```\n\nIf you are using [Expects syntax](#expects-syntax) together with `subject`, you can use the `is_expected` helper:\n\n```elixir\ndescribe \"Numbers\" do\n  subject do: 5\n\n  context \"straight up, no message\" do\n    it is_expected |\u003e to_eq 5\n  end\n\n  context \"you can also use a custom message\" do\n    it \"is equal to 5\" do\n      is_expected |\u003e to_eq 5\n    end\n  end\nend\n```\n\nBy default, every created context via `describe` or `context` contains a `subject` that returns `nil`.\n\n## Expects syntax\n\nYou may use the regular ExUnit `assert` syntax if you wish, but Pavlov includes\nan `expect` syntax that makes your tests more readable.\n\nIf you wish to use this syntax, simply import the `Pavlov.Syntax.Expect` at the\nbeginning of your Test module:\n\n```elixir\ndefmodule MyTest do\n  use Pavlov.Case, async: true\n  import Pavlov.Syntax.Expect\n  #...\nend\n```\n\nAll core matchers are supported under both syntaxes.\n\n## Included Matchers\n\nWhen using the `expects` syntax, all matchers have negative counterparts, ie:\n```elixir\nexpect 1 |\u003e not_to_eq 2\nexpect(1 \u003e 5) |\u003e not_to_be_true\n```\n\nVisit the [Pavlov Wiki](https://github.com/sproutapp/pavlov/wiki/Included-Matchers)\nto learn more about all of the core matchers available for your tests.\n\n## Callbacks\nFor now, Pavlov only supports callbacks that run before test cases. [ExUnit's\n`on_exit` callback](http://elixir-lang.org/docs/stable/ex_unit/ExUnit.Callbacks.html#on_exit/2) is still fully supported though, and may be used normally inside your `before` callbacks.\n\n### before(:each)\nRuns the specified code before every test case.\n\n```elixir\ndescribe \"before :each\" do\n  before :each do\n    IO.puts \"A test is about to start\"\n    :ok\n  end\n\n  it \"does something\" do\n    #...\n  end\n\n  it \"does something else\" do\n    #...\n  end\nend\n```\n\nIn this case, `\"A test is about to start\"` is printed twice to the console.\n\n### before(:all)\nRuns the specified code once before any tests run.\n\n```elixir\ndescribe \"before :all\" do\n  before :all do\n    IO.puts \"This suite is about to run\"\n    :ok\n  end\n\n  it \"does something\" do\n    #...\n  end\n\n  it \"does something else\" do\n    #...\n  end\nend\n```\nIn this case, `\"This suite is about to run\"` is printed once to the console.\n\n## Mocking\nPavlov provides facilities to mock functions in your Elixir modules. This is\nachieved using [Meck](https://github.com/eproxus/meck), an erlang mocking tool.\n\nHere's a simple example using [HTTPotion](https://github.com/myfreeweb/httpotion):\n\n```elixir\nbefore :each do\n  allow HTTPotion |\u003e to_receive(get: fn(url) -\u003e \"\u003chtml\u003e\u003c/html\u003e\" end)\nend\n\nit \"gets a page\" do\n  result = HTTPotion.get(\"http://example.com\")\n\n  expect HTTPotion |\u003e to_have_received :get\n  expect result |\u003e to_eq \"\u003chtml\u003e\u003c/html\u003e\"\nend\n```\n\nIf you want the mock to retain all other functions in the original module,\nthen you will need to pass the `opts` `List` argument to the `allow` function\nand include the `:passthrough` value. The `allow` function specifies a default\n`opts` `List` that includes the `:no_link` value. This value should be included\nin the `List` as it ensures that the mock (which is linked to the creating\nprocess) will unload automatically when a crash occurs.\n\n```elixir\nbefore :each do\n  allow(HTTPotion, [:no_link, :passthrough]) |\u003e to_receive(get: fn(url) -\u003e \"\u003chtml\u003e\u003c/html\u003e\" end)\nend\n```\n\nExpectations on mocks also work using `asserts` syntax via the `called` matcher:\n\n```elixir\nbefore :each do\n  allow HTTPotion |\u003e to_receive(get: fn(url) -\u003e \"\u003chtml\u003e\u003c/html\u003e\" end)\nend\n\nit \"gets a page\" do\n  HTTPotion.get(\"http://example.com\")\n\n  assert called HTTPotion.get\nend\n```\n\n### Mocks with arguments\nYou can also perform assertions on what arguments were passed to a mocked\nmethod:\n\n```elixir\nbefore :each do\n  allow HTTPotion |\u003e to_receive(get: fn(url) -\u003e \"\u003chtml\u003e\u003c/html\u003e\" end)\nend\n\nit \"gets a page\" do\n  HTTPotion.get(\"http://example.com\")\n\n  expect HTTPotion |\u003e to_have_received :get |\u003e with_args \"http://example.com\"\nend\n```\n\nIn `asserts` syntax:\n\n```elixir\nbefore :each do\n  allow HTTPotion |\u003e to_receive (get: fn(url) -\u003e url end )\nend\n\nit \"gets a page\" do\n  HTTPotion.get(\"http://example.com\")\n\n  assert called HTTPotion.get(\"http://example.com\")\nend\n```\n\n## Skipping tests\nPavlov runs with the `--exclude pending:true` configuration by default, which\nmeans that tests tagged with `:pending` will not be run.\n\nPavlov offers several convenience methods to skip your tests, BDD style:\n\n### xit\nMarks a specific test as pending and will not run it.\n\n```elixir\nxit \"does not run\" do\n  # This will never run\nend\n```\n\n### xdescribe/xcontext\nMarks a group of tests as pending and will not run them. Just as `describe`\nand `context`, `xdescribe` and `xcontext` are analogous.\n\n```elixir\nxdescribe \"A pending group\" do\n  it \"does not run\" do\n    # This will never run\n  end\n\n  it \"does not run either\" do\n    # This will never run either\n  end\nend\n```\n\n## Development\n\nAfter cloning the repo, make sure to download all dependencies using `mix deps.get`.\nPavlov is tested using Pavlov itself, so the general philosophy is to simply write a test using a given feature until it passes.\n\n### Running the tests\nSimply run `mix test`\n\n### Building the docs\nRun `MIX_ENV=docs mix docs`. The resulting HTML files will be output to the `docs` folder.\n\n## Contributing\n\n1. Fork it ( https://github.com/sproutapp/pavlov/fork )\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create a new Pull Request\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsproutapp%2Fpavlov","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsproutapp%2Fpavlov","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsproutapp%2Fpavlov/lists"}