{"id":13509174,"url":"https://github.com/antonmi/espec","last_synced_at":"2025-05-13T23:02:29.461Z","repository":{"id":28879765,"uuid":"32404260","full_name":"antonmi/espec","owner":"antonmi","description":"Elixir Behaviour Driven Development","archived":false,"fork":false,"pushed_at":"2025-02-26T12:25:48.000Z","size":1372,"stargazers_count":807,"open_issues_count":10,"forks_count":63,"subscribers_count":30,"default_branch":"main","last_synced_at":"2025-04-30T12:16:50.502Z","etag":null,"topics":["bdd","bdd-specs","elixir","testing"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/antonmi.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-03-17T16:06:01.000Z","updated_at":"2025-04-20T12:03:02.000Z","dependencies_parsed_at":"2025-03-16T08:00:26.221Z","dependency_job_id":"727a8679-7b29-4ce5-965e-6e7ed9bea153","html_url":"https://github.com/antonmi/espec","commit_stats":{"total_commits":870,"total_committers":54,"mean_commits":16.11111111111111,"dds":0.4747126436781609,"last_synced_commit":"fa3802afa58acc2d3735648c259d9c4bd417ed55"},"previous_names":[],"tags_count":71,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antonmi%2Fespec","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antonmi%2Fespec/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antonmi%2Fespec/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antonmi%2Fespec/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/antonmi","download_url":"https://codeload.github.com/antonmi/espec/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254040142,"owners_count":22004453,"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":["bdd","bdd-specs","elixir","testing"],"created_at":"2024-08-01T02:01:04.033Z","updated_at":"2025-05-13T23:02:29.380Z","avatar_url":"https://github.com/antonmi.png","language":"Elixir","readme":"# ESpec\n[![Hex.pm](https://img.shields.io/hexpm/v/espec.svg?style=flat-square)](https://hex.pm/packages/espec)\n\n## ESpec is a BDD testing framework for Elixir.\n\nESpec is inspired by RSpec and the main idea is to be close to its perfect DSL.\n\nIt is NOT a wrapper around ExUnit but a completely new testing framework written from scratch.\n\n## Features\n  * Test organization with `describe`, `context`, `it`, and etc blocks.\n  * Familiar matchers: `eq`, `be_close_to`, `raise_exception`, etc.\n  * Possibility to add custom matchers.\n  * There are two types of expectation syntax:\n    - `expect` syntax with pipe operator `expect smth1 |\u003e to(eq smth2)` or `is_expected |\u003e to(eq smth)` when `subject` is defined;\n    - `should` syntax: `smth1 |\u003e should(eq smth2)` or `should eq smth` when `subject` is defined.\n    \n    ##### Note: RSpec syntax `expect(smth1).to eq(smth2)` is deprecated and won't work with OTP 21.\n  * `before` and `finally` blocks (like RSpec `before` and `after`).\n  * `let`, `let!` and `subject`.\n  * Shared examples.\n  * Generated examples.\n  * Async examples.\n  * Mocks with Meck.\n  * Doc specs.\n  * HTML and JSON outputs.\n  * Etc and etc.\n\n## Contents\n- [Installation](#installation)\n- [Run specs](#run-specs)\n- [Context blocks and tags](#context-blocks-and-tags)\n- [Examples](#examples)\n- [Filters](#filters)\n- [`before` and `finally`](#before-and-finally)\n- [`before_all` and `after_all`](#before_all-and-after_all)\n- [`shared` data](#shared-data)\n- [`let` and `subject`](#let-and-subject)\n- [Shared examples](#shared-examples)\n- [Generated examples](#generated-examples)\n- [Async examples](#async-examples)\n- [Matchers](#matchers)\n- [`assert` and `refute`](#assert-and-refute)\n- [`assert_receive` and `refute_receive`](#assert_receive-and-refute_receive)\n- [`capture_io` and `capture_log`](#capture_io-and-capture_log)\n- [Custom matchers](#custom-matchers)\n- [`described_module`](#described_module)\n- [Mocks](#mocks)\n- [Datetime Comparison](#datetime-comparison)\n- [Doc specs](#doc-specs)\n- [Configuration and options](#configuration-and-options)\n- [Formatters](#formatters)\n- [Changelog](#changelog)\n- [Contributing](#contributing)\n\n## Installation\n\nAdd `espec` to dependencies in the `mix.exs` file:\n\n```elixir\ndef deps do\n  ...\n  {:espec, \"~\u003e 1.9.2\", only: :test},\n  ...\nend\n```\n```sh\nmix deps.get\n```\nThen run:\n```sh\nMIX_ENV=test mix espec.init\n```\nThe task creates `spec/spec_helper.exs`\n\nSet `preferred_cli_env` for `espec` in the `mix.exs` file:\n\n```elixir\ndef project do\n  ...\n  preferred_cli_env: [espec: :test],\n  ...\nend\n```\n\nOr run with `MIX_ENV=test`:\n```sh\nMIX_ENV=test mix espec\n```\n\nPlace your `_spec.exs` files into `spec` folder. `use ESpec` in the 'spec module'.\n```elixir\ndefmodule SyntaxExampleSpec do\n  use ESpec\n  it do: expect true |\u003e to(be_true())\n  it do: (1..3) |\u003e should(have 2)\nend\n```\n\n## Run specs\n```sh\nmix espec\n```\nRun specific spec:\n```sh\nmix espec spec/some_spec.exs:25\n```\n\nRead the help:\n```sh\nMIX_ENV=test mix help espec\n```\n\n## Context blocks and tags\nThere are three macros with the same functionality: `context`, `describe`, and `example_group`.\n\nContext can have description and tags.\n```elixir\ndefmodule ContextSpec do\n  use ESpec\n\n  example_group do\n    context \"Some context\" do\n      it do: expect \"abc\" |\u003e to(match ~r/b/)\n    end\n\n    describe \"Some another context with opts\", focus: true do\n      it do: 5 |\u003e should(be_between 4, 6)\n    end\n  end\nend\n```\nAvailable tags are:\n  * `skip: true` or `skip: \"Reason\"` - skips examples in the context;\n  *  `focus: true` - sets focus to run with `--focus ` option.\n\nThere are also `xcontext`, `xdescribe`, `xexample_group` macros to skip example groups.\nAnd `fcontext`, `fdescribe`, `fexample_group` for focused groups.\n\n'spec' module is also a context with module name as description. One can add tags for this context after `use ESpec:`\n```elixir\ndefmodule ContextTagsSpec do\n  use ESpec, skip: \"Skip all examples in the module\"\n  ...\nend\n```\n## Examples\n\n`example`, `it`, and `specify` macros define the 'spec example'.\n```elixir\ndefmodule ExampleAliasesSpec do\n  use ESpec\n\n  example do: expect [1,2,3] |\u003e to(have_max 3)\n\n  it \"Test with description\" do\n    4.2 |\u003e should(be_close_to 4, 0.5)\n  end\n\n  specify \"Test with options\", [pending: true], do: \"pending\"\nend\n```\nYou can use `skip`, `pending` or `focus` tags to control evaluation.\nThere are also macros:\n* `xit`, `xexample`, `xspecify` - to skip;\n* `fit`, `fexample`, `fspecify`, `focus` - to focus;\n* `pending/1`, `example/1`, `it/1`, `specify/1` - for pending examples.\n```elixir\ndefmodule ExampleTagsSpec do\n  use ESpec\n\n  xit \"skip\", do: \"skipped\"\n  focus \"Focused\", do: \"Focused example\"\n\n  it \"pending example\"\n  pending \"it is also pending example\"\nend\n```\n\n## Filters\nThe are `--only`, `--exclude` and `--string` command line options.\n\nOne can tag example or context and then use `--only` or `--exclude` option to run (or exclude) tests with specific tag.\n```elixir\ndefmodule FiltersSpec do\n  use ESpec\n\n  context \"context with tag\", context_tag: :some_tag do\n    it do: \"some example\"\n    it \"example with tag\", example_tag: true do\n     \"another example\"\n    end\n  end\nend\n```\n```sh\nmix espec spec/some_spec.exs --only context_tag:some_tag --exclude example_tag\n```\nThis runs only one test \"some example\"\n\nYou can also filter examples by `--string` option which filter examples which contain given string in their nested description.\n```sh\nmix espec spec/some_spec.exs --string 'context with tag'\n```\n\n## `before` and `finally`\n`before` blocks are evaluated before the example and `finally` runs after the example.\n\nThe blocks can return `{:shared, key: value, ...}` or (like in ExUnit) `{:ok, key: value, ...}`, so the keyword list will be saved in the dictionary and can be accessed in other `before` blocks, in the example, and in `finally` blocks through [`shared`](#shared-data).\nYou can also use a map as a second term in returned tuple: `{:shared, %{key: value, ...}}`.\n\nExample:\n```elixir\ndefmodule BeforeAndFinallySpec do\n  use ESpec\n\n  before do: {:shared, a: 1}\n\n  context \"Context\" do\n    before do: {:shared, %{b: shared[:a] + 1}}\n    finally do: \"#{shared[:b]} == 2\"\n\n    it do: shared.a |\u003e should(eq 1)\n    it do: shared.b |\u003e should(eq 2)\n\n    finally do: \"This finally will not be run. Define 'finally' before the example\"\n  end\nend\n```\nNote, that `finally` blocks must be defined before the example.\nAlso note that `finally` blocks are executed in reverse order. Please see 'spec/before_finally_order_spec.exs' to figure out details.\n\nThere is also a short form of 'before' macro which allows to fill in shared dictionary:\n```elixir\nbefore a: 1, b: 2\n# which is equivalent to\nbefore do: {shared: a: 1, b: 2}\n```\nYou can configure 'global' `before` and `finally` in `spec_helper.exs`:\n```elixir\nESpec.configure fn(config) -\u003e\n  config.before fn(tags) -\u003e {:shared, answer: 42, tags: tags} end  #can assign values in dictionary\n  config.finally fn(shared) -\u003e shared.answer  end     #can access assigns\nend\n```\nThese functions will be called before and after each example which ESpec runs.\n\n`config.before` accepts example tags as an argument. So all example tags (including tags from parent contexts) are available in `config.before`. This allows you to run some specific pre-configuration based on tags.\n```elixir\nESpec.configure fn(config) -\u003e\n  config.before fn(tags) -\u003e\n    if tags[:async] || tags[:custom_tag] == :do_like_async\n      PrepareAsyncExecution.setup\n    end\n    {:shared, tags: tags}\n  end\nend\n```\n## `before_all` and `after_all`\nThere are hooks that evaluate before and after all the examples in a module. Use this hooks for complex system setup and tear down.\n```elixir\ndefmodule BeforeAllSpec do\n  use ESpec\n\n  before_all do\n    RocketLauncher.start_the_system!\n  end\n\n  it do: ...\n  it do: ...\n\n  after_all do\n    RocketLauncher.stop_the_system!\n  end\nend\n```\nNote, `before_all` and `after_all` hooks do not set `shared` data and do not have access to them. Also note that you can define only one `before_all` and one `after_all` hook in a spec module.\n\n## 'shared' data\n`shared` is used to share data between spec blocks. You can access data by `shared.some_key` or `shared[:some_key]`.\n`shared.some_key` will raise exception if the key 'some_key' does not exist, while `shared[:some_key]` will return `nil`.\n\nThe `shared` variable appears in your `before`, `finally`, in `config.before` and `config.finally`, in `let` and `example` blocks.\n\n`before` and `finally` blocks (including 'global') can modify the dictionary when return `{:shared, key: value}`.\nThe example below illustrates the life-cycle of `shared`:\n\n`spec_helper.exs`\n```elixir\nESpec.start\n\nESpec.configure fn(config) -\u003e\n  config.before fn -\u003e {:shared, answer: 42} end         # shared == %{answer: 42}\n  config.finally fn(shared) -\u003e IO.puts shared.answer  end    # it will print 46\nend\n```\n`some_spec.exs`:\n```elixir\ndefmodule SharedBehaviorSpec do\n  use ESpec\n\n  before do: {:shared, answer: shared.answer + 1}          # shared == %{answer: 43}\n  finally do: {:shared, answer: shared.answer + 1}         # shared == %{answer: 46}\n\n  context do\n    before do: {:shared, answer: shared.answer + 1}        # shared == %{answer: 44}\n    finally do: {:shared, answer: shared.answer + 1}       # shared == %{answer: 45}\n    it do: shared.answer |\u003e should(eq 44)\n  end\nend\n```\nSo, 'config.finally' will print `46`.\nPay attention to how `finally` blocks are defined and evaluated.\n\n## `let` and `subject`\n`let` and `let!` have the same behavior as in RSpec. Both defines memoizable functions in 'spec module'. The value will be cached across multiple calls in the same example but not across examples. \n**`let` is lazy-evaluated, it is not evaluated until the first time the function it defines is invoked.**\nUse `let!` to force the invocation before each example.\nA bang version is just a shortcut for:\n```elixir\nlet :a, do: 1\nbefore do: a()\n```\nIn example below, `let! :a` will be evaluated just after `before a: 1`. But `let :b` will be invoked only in the last test.\n```elixir\ndefmodule LetSpec do\n  use ESpec\n\n  before a: 1\n  let! :a, do: shared.a\n  let :b, do: shared.a + 1\n\n  it do: expect a() |\u003e to(eq 1)\n  it do: expect b() |\u003e to(eq 2)\nend\n```\nNote, The `shared` is available in `let`s but neither `let` nor `let!` can modify the dictionary.\n\nYou can pass a keyword list to `let` or `let!` to define several 'lets' at once:\n```elixir\ndefmodule LetSpec do\n  use ESpec\n\n  let a: 1, b: 2\n\n  it do: expect a() |\u003e to(eq 1)\n  it do: expect b() |\u003e to(eq 2)\nend\n```\nNote, `subject` and `subject!` are just aliases for `let :subject, do: smth` and `let! :subject, do: smth`. You can use `is_expected` macro (or a simple `should` expression) when `subject` is defined.\n```elixir\ndefmodule SubjectSpec do\n  use ESpec\n\n  subject(1 + 1)\n  it do: is_expected() |\u003e to(eq 2)\n  it do: should(eq 2)\n\n  context \"with block\" do\n    subject do: 2 + 2\n    it do: is_expected() |\u003e to_not(eq 2)\n    it do: should_not(eq 2)\n  end\nend\n```\nThere are helpers that can help you assign values from expressions that return {:ok, result} or {:error, result} tuples. For example, `File.read\\1` returns {:ok, binary} or {:error, reason}.\n\nThere are `let_ok` (`let_ok!`) and `let_error` (`let_error!`) functions that allow you assign values easily:\n```elixir\nlet_ok :file_binary, do: File.read(\"file.txt\")\nlet_error :error_reason, do: File.read(\"error.txt\")\n```\n## Shared Examples\nOne can reuse the examples defined in spec module.\n```elixir\ndefmodule SharedSpec do\n  use ESpec, shared: true\n\n  subject shared.hello\n  it do: should eq(\"world!\")\nend\n```\n`shared: true` marks examples in the module as shared, so the examples will be skipped until you reuse them.\nYou can use the examples with `it_behaves_like` or its alias `include_examples` macro:\n```elixir\ndefmodule UseSharedSpec do\n  use ESpec\n\n  before hello: \"world!\"\n  it_behaves_like(SharedSpec)\n  #or\n  include_examples(SharedSpec)\nend\n```\nYou can also use `let` variables from parent module in shared examples.\nUse `let_overridable` macro to define `let` which will be overridden.\nYou can pass single atom, list of atoms, or keyword with default values.\nSee examples below.\n```elixir\ndefmodule SharedSpec do\n  use ESpec, shared: true, async: true\n\n  let_overridable a: 10, b: 20\n  let_overridable [:c, :d]\n  let_overridable :e\n\n  let :internal_value, do: :shared_spec\n\n  it \"will be overridden\" do\n    expect a() |\u003e to(eq 1)\n    expect c() |\u003e to(eq 3)\n    expect e() |\u003e to(eq 5)\n  end\n\n  it \"returns defaults\" do\n    expect b() |\u003e to(eq 20)\n    expect d() |\u003e to(eq nil)\n  end\n\n  it \"does not override internal 'lets'\" do\n    expect internal_value() |\u003e to(eq :shared_spec)\n  end\nend\n\ndefmodule LetOverridableSpec do\n  use ESpec, async: true\n\n  let :internal_value, do: :some_spec\n\n  it_behaves_like(SharedSpec, a: 1, c: 3, e: 5)\nend\n```\n\n### Shared Examples in separate files\n\nIn case you want to add some \"global\" shared specs which you want to use in multiple specs, ESpec has you covered. Simply add these files to your `spec/shared` folder. The place where `mix espec.init` generates you a placeholder folder and file.\n\nBy default ESpec loads all files contained in `\u003cyour_spec_paths\u003e/shared` which match your `spec_pattern`.\nThe [Configuration and options](#configuration-and-options) chapter contains details on how to control this behaviour.\n\nIn case you already use `Code.require_file/1` in your `spec_helper.exs` don't sweat. ESpec makes sure to require each file only once, it ignores files which already have been included.\n\n\n## Generated examples\nExamples can be generated from code \"templates\". This should help with making the code more DRY:\n```elixir\ndefmodule GeneratedExamplesSpec do\n  use ESpec, async: true\n\n  subject(24)\n\n  Enum.map 2..4, fn(n) -\u003e\n    it \"is divisible by #{n}\" do\n      expect rem(subject(), unquote(n)) |\u003e to(eq 0)\n    end\n  end\nend\n```\n\nPlease mind the `unquote` call above - if you forget to `unquote` the `n` variable the compiler will show some warnings about it missing and eventually stop with an error: `undefined function n/0`.\n\n## Async examples\nThere is an `async: true` option you can set for the context or for the individual example:\n```elixir\ndefmodule AsyncSpec do\n  use ESpec, async: true\n  it do: \"async example\"\n\n  context \"Sync\", async: false do\n    it do: \"sync example\"\n\n    it \"async again\", async: true do\n      \"async\"\n    end\n  end\nend\n```\nThe examples will be partitioned into two queries. Examples in asynchronous query will be executed in parallel in different processes.\n\nDon't use `async: true` if you change the global state in your specs!\n\n## Matchers\n#### Equality\n```elixir\nexpect actual |\u003e to(eq expected)  # passes if actual == expected\nexpect actual |\u003e to(eql expected) # passes if actual === expected\nexpect actual |\u003e to(be_close_to expected, delta)\nexpect actual |\u003e to(be_between hard_place, rock)\n```\n#### Comparisons\nCan be used with `:\u003e`, `:\u003c`, `:\u003e=`, `:\u003c=`, and etc.\n```elixir\nexpect actual |\u003e to(be operator, value)\n```\nPasses if `apply(Kernel, operator, [actual, value]) == true`\n\n#### Patterns\n```elixir\nexpect actual |\u003e to(match_pattern {:ok, _}) # {:ok, _} = actual\n```\nIt's not possible to call functions in the pattern and use the return value as\npattern (`{:ok, function()}`), this obviously means no `let` functions. If you\nneeed to use the return value of a function, use a variable:\n\n```elixir\nvalue = function()\n\nexpect actual |\u003e to(match_pattern {:ok, ^value})\n```\n#### Booleans\n```elixir\nexpect actual |\u003e to(be_true())\nexpect actual |\u003e to(be_truthy())\nexpect actual |\u003e to(be_false())\nexpect actual |\u003e to(be_falsy())\n```\n#### Regular expressions\n```elixir\nexpect actual |\u003e to(match ~r/expression/)\nexpect actual |\u003e to(match \"string\")\n```\n#### Enumerable\nThere are many helpers to test enumerable collections:\n```elixir\nexpect collection |\u003e to(be_empty()) # Enum.count(collection) == 0\n... have value                      # Enum.member?(collection, value)\n... have_all func                   # Enum.all?(collection, func)\n... have_any func                   # Enum.any?(collection, func)\n... have_at position, value         # Enum.at?(collection, position) == value\n... have_count value                # Enum.count(collection) == value\n... have_size value                 # alias\n... have_length value               # alias\n... have_count_by func, value       # Enum.count(collection, func) == value\n... have_max value                  # Enum.max(collection) == value\n... have_max_by func, value         # Enum.max_by(collection, fun) == value\n... have_min value                  # Enum.min(collection) == value\n... have_min_by func, value         # Enum.min_by(collection, fun) == value\n```\n#### List\n```elixir\nexpect list |\u003e to(have_first value)  # List.first(list) == value\n... have_last value                  # List.last(list) == value\n... have_hd value                    # hd(list) == value\n... have_tl value                    # tl(list) == value\n... contain_exactly value            # Keyword.equals?(list, value)\n... match_list value                 # alias for contain_exactly\n```\n#### Binary\n```elixir\nexpect binary |\u003e to(have_byte_size value) # byte_size(binary) == value\n```\n#### String\n```elixir\nexpect string |\u003e to(have_first value)   # String.first(string) == value\n... have_last value                     # String.last(string) == value\n... start_with value                    # String.starts_with?(string, value)\n... end_with value                      # String.end_with?(string, value)\n... have value                          # String.contains?(string, value)\n... have_at pos, value                  # String.at(string, pos) == value\n... have_length value                   # String.length(string) == value\n... have_size value                     # alias\n... have_count value                    # alias\n... be_valid_string()                   # String.valid?(string)\n... be_printable()                      # String.printable?(string)\n... be_blank()                          # String.length(string) == 0\n... be_empty()                          # String.length(string) == 0\n```\n#### Map\n```elixir\nexpect map |\u003e to(have foo: \"bar\")     # Map.get(map, :foo) == \"bar\"\nexpect map |\u003e to(have {:foo, \"bar\"})  # Map.get(map, :foo) == \"bar\"\nexpect map |\u003e to(have {\"foo\", \"bar\"}) # Map.get(map, :foo) == \"bar\"\nexpect map |\u003e to(have_key value)      # Map.has_key?(map, value)\nexpect map |\u003e to(have_value value)    # Enum.member?(Map.values(map), value)\n```\n#### PID\n```elixir\nexpect pid |\u003e to(be_alive) # Process.alive?(pid)\n```\n\n`have` also works for Structs.\n\n#### Type checking\n``` elixir\nexpect :espec |\u003e to(be_atom)  #is_atom(:espec) == true\n... be_binary()\n... be_bitstring()\n... be_boolean()\n... be_integer()\n... be_float()\n... be_number()\n... ...\n... ...\n... be_tuple()\n... be_list()\n... be_map()\n... be_port()\n... be_pid()\n... be_reference()\n... be_function()\n... be_function arity\n... be_struct()\n... be_struct StructExample\n```\n#### Exceptions\n```elixir\nexpect function |\u003e to(raise_exception())\nexpect function |\u003e to(raise_exception ErrorModule)\nexpect function |\u003e to(raise_exception ErrorModule, \"message\")\n```\n#### Throws\n```elixir\nexpect function |\u003e to(throw_term())\nexpect function |\u003e to(throw_term term)\n```\n#### Change function's return value\nTest if call of function1 change the function2 returned value to smth or from to smth\n```elixir\nexpect function1 |\u003e to(change function2)\nexpect function1 |\u003e to(change function2, to)\nexpect function1 |\u003e to(change function2, from, to)\nexpect function1 |\u003e to(change function2, by: value)\n```\n#### Check result\nTest if function returns `{:ok, result}` or `{:error, reason}` tuple\n```elixir\nexpect {:ok, :the_result} |\u003e to(be_ok_result())\nexpect {:error, :an_error} |\u003e to(be_error_result())\n```\n\n## `assert` and `refute`\nIf you are missing ExUnit `assert` and `refute`, ESpec has such functions as aliases to `be_truthy` and `be_falsy`\n```elixir\ndefmodule AssertAndRefuteSpec do\n  use ESpec\n\n  it \"asserts\" do\n    assert \"ESpec\"\n    #expect \"ESpec\" |\u003e to(be_truthy())\n  end\n\n  it \"refutes\" do\n    refute nil\n    #expect nil |\u003e to(be_falsy())\n  end\nend\n```\n\n## `assert_receive` and `refute_receive`\n`assert_receive` (`assert_received`) and `refute_receive` (refute_received) work identically to ExUnit ones.\n\n`assert_receive` asserts that a message matching pattern was or is going to be received within timeout.\n`assert_received` asserts that a message was received and is in the current process mailbox. It is the same as `assert_receive` with 0 timeout.\n\n`refute_receive` asserts that a message matching pattern was not received and won’t be received within the timeout.\n`refute_received` asserts that a message was not received (`refute_receive` with 0 timeout).\n\nThe default timeout for `assert_receive` and `refute_receive` is 100ms. You can pass custom timeout as a second argument.\n```elixir\ndefmodule AssertReceviveSpec do\n  use ESpec\n\n  it \"demonstrates assert_received\" do\n    send(self(), :hello)\n    assert_received :hello\n  end\n\n  it \"demonstrates assert_receive with custom timeout\" do\n    parent = self()\n    spawn(fn -\u003e :timer.sleep(200); send(parent, :hello) end)\n    assert_receive(:hello, 300)\n  end\n\n  it \"demonstrates refute_receive\" do\n    send(self(), :another_hello)\n    refute_receive :hello_refute\n  end\nend\n```\n## `capture_io` and `capture_log`\n`capture_io` and `capture_log` are just copied from ExUnit and designed to test IO or Logger output:\n```elixir\ndefmodule CaptureSpec do\n  use ESpec\n\n  it \"tests capture_io\" do\n    message = capture_io(fn -\u003e IO.write \"john\" end)\n    message |\u003e should(eq \"john\")\n  end\n\n  it \"tests capture_log\" do\n    message = capture_log(fn -\u003e Logger.error \"log msg\" end)\n    expect message |\u003e to(match \"log msg\")\n  end\nend\n```\n## Custom matchers\nYou can define your own matchers!\nThe only functions you should implement is `match/2`, `success_message/4`, and `error_message`.\nRead the [wiki page](https://github.com/antonmi/espec/wiki/Custom-matchers) for detailed instructions.\nThere is an example in [custom_assertion_spec.exs](https://github.com/antonmi/espec/blob/master/spec/assertions/custom_assertion_spec.exs).\n\n#### Extensions\nThere are community supported projects with sets of mathers:\n- [test_that_json_espec](https://github.com/facto/test_that_json_espec)\n- [espec_json_api_matchers](https://github.com/MYOB-Technology/espec_json_api_matchers)\n- [bamboo_espec](https://github.com/facto/bamboo_espec)\n\n## described_module\nIf you keep the naming convention 'module TheModuleSpec is spec for TheModule' you can access tested module by `described_module()` helper.\n```elixir\ndefmodule TheModule do\n  def fun, do: :fun\nend\n\ndefmodule TheModuleSpec do\n  use ESpec\n  it do: expect described_module().fun |\u003e to(eq :fun)\nend\n```\n\n## Mocks\nESpec uses [Meck](https://github.com/eproxus/meck) to mock functions.\nYou can mock the module with 'allow accept':\n```elixir\ndefmodule MocksSpec do\n  use ESpec\n  context \"with old syntax\" do\n    before do: allow SomeModule |\u003e to(accept(:func, fn(a, b) -\u003e a + b end))\n    it do: expect SomeModule.func(1, 2) |\u003e to(eq 3)\n  end\n\n  context \"with new syntax\" do\n    before do: allow SomeModule |\u003e to(accept :func, fn(a, b) -\u003e a + b end)\n    it do: expect SomeModule.func(1, 2) |\u003e to(eq 3)\n  end\nend\n```\nIf you don't specify the function to return ESpec creates stubs with arity `0` and `1`:\n`fn -\u003e end` and `fn(_) -\u003e end`, which return `nil`.\n```elixir\ndefmodule DefaultMocksSpec do\n  use ESpec\n  before do: allow SomeModule |\u003e to(accept :func)\n  it do: expect SomeModule.func |\u003e to(be_nil())\n  it do: expect SomeModule.func(42) |\u003e to(be_nil())\nend\n```\nYou can also use pattern matching in your mocks:\n```elixir\ndefmodule PatternMockSpec do\n  use ESpec\n  before do\n    args = {:some, :args}\n    allow SomeModule |\u003e to(accept :func, fn(^args) -\u003e {:ok, :success} end)\n  end\n\n  it do: expect SomeModule.func({:some, :args}) |\u003e to(be_ok_result())\n\n  it \"raises exception when does not match\" do\n    expect(fn -\u003e SomeModule.func({:wrong, :args}) end)\n    |\u003e to(raise_exception FunctionClauseError)\n  end\nend\n```\nBehind the scenes 'allow accept' makes the following:\n```elixir\n:meck.new(module, [:non_strict, :passthrough])\n:meck.expect(module, name, function)\n```\nFind the explanation about the `:non_strict` and `:passthrough` options [here](https://github.com/eproxus/meck/blob/master/src/meck.erl).\nThe default options (`[:non_strict, :passthrough]`) can be overridden:\n```elixir\nallow SomeModule |\u003e to(accept :func, fn(a,b) -\u003e a + b end, [:non_strict, :unstick])\n```\nAll the mocked modules are unloaded with `:meck.unload(modules)` after each example.\n\nYou can also pass a list of atom-function pairs to the `accept` function:\n```elixir\nallow SomeModule |\u003e to(accept f1: fn -\u003e :f1 end, f2: fn -\u003e :f2 end)\n```\nOne can use `passthrough/1` function to call the original function:\n```elixir\n  before do\n    allow SomeModule |\u003e to(accept(:fun, fn\n      :mocked -\u003e \"mock!\"\n      _ -\u003e passthrough([args])\n    end))\n  end\n\n  it do: expect SomeModule.fun(:mocked) |\u003e to(eq \"mock!\")\n  it do: expect SomeModule.fun(2) |\u003e to(eq 3)\n```\nThe `passthrough/1` just calls the `:meck.passthrough/1` from the `:meck` module.\n\nThere is also an expectation to check if the module accepted a function call:\n```elixir\naccepted(func, args \\\\ :any, opts \\\\ [pid: :any, count: :any])\n```\nSo, the options are:\n- test if the function is called with some particular arguments of with `any`;\n- specify the `pid` of the process which called the function;\n- test the count of function calls.\n\n```elixir\ndefmodule MockOptionsSpec do\n  use ESpec\n  before do\n    allow SomeModule |\u003e to(accept :func, fn(a,b) -\u003e a + b end)\n    SomeModule.func(1, 2)\n  end\n\n  it do: expect SomeModule |\u003e to(accepted :func)\n  it do: expect SomeModule |\u003e to(accepted :func, [1,2])\n\n  describe \"with options\" do\n    defmodule Server do\n      def call(a, b) do\n        ESpec.SomeModule.func(a, b)\n        ESpec.SomeModule.func(a, b)\n      end\n    end\n\n    before do\n      pid = spawn(Server, :call, [1, 2])\n      :timer.sleep(100)\n      {:ok, pid: pid}\n    end\n\n    it do: expect ESpec.SomeModule |\u003e to(accepted :func, [1,2], pid: shared.pid, count: 2)\n  end\nend\n```\n`accepted` assertion checks `:meck.history(SomeModule)`. See [meck](https://github.com/eproxus/meck) documentation.\n\nDon't use `async: true` when using mocks!\n\n### Datetime Comparison\n\nESpec has comparison support for Elixir's date(time) related structs. Specifically, it has support for Date, Time, NaiveDateTime, and DateTime structs using ESpec's `be_close_to` and `be` assertions. It allows you to compare using the lowest-level granularity available in the struct. For example, since the lowest level of granularity available in a NaiveDateTime is the microsecond, you can compare how close to NaiveDateTime structs are with respect to microseconds.\n\n#### Datetime be assertion(s) syntax\n\nFor the `be` assertions, ESpec supports a syntax with a granularity tuple (or keyword list) or a syntax without it. The following examples are shown with a Date struct.\n\n##### Be assertion syntax without granularity\n\n```\nit do: expect ~D[2020-08-07] |\u003e to(be :\u003e=, ~D[2017-08-07])\n```\n\n##### Be assertion syntax with granularity\n\n```\nit do: expect ~D[2020-08-07] |\u003e to(be :\u003e=, ~D[2017-08-07], {:years, 3})\n\n# or alternatively, you can do:\nit do: expect ~D[2020-08-07]) |\u003e to(be :\u003e=, ~D[2017-08-07], years: 3)\n```\n\n#### Datetime be_close_to asssertion(s) syntax\n\n##### Date Struct Comparison Example(s)\n\n```\nexpect ~D[2017-08-07] |\u003e to(be_close_to(~D[2018-08-07], {:years, 1}))\n\n# or alternatively, you can do:\nit do: expect ~D[2017-08-07] |\u003e to(be_close_to(~D[2020-08-07], {:years, 3}))\n```\n\n##### NaiveDateTime Struct Comparison Example\n\n```\nexpect ~N[2017-08-07 01:10:10.000001] |\u003e to(be_close_to(~N[2017-08-07 01:10:10.000003], {:microseconds, 2}))\n\n# or alternatively, you can do:\nit do: expect ~N[2017-08-07 01:10:10.000001] |\u003e to(be_close_to(~N[2017-08-07 01:10:10.000003], {:microseconds, 2}))\n```\n\n##### Time Struct Comparison Example\n\n```\nexpect ~T[01:10:10] |\u003e to(be_close_to(~T[01:50:10], {:minutes, 40}))\n\n# or alternatively, you can do:\nit do: expect ~T[01:10:10] |\u003e to(be_close_to(~T[01:50:10], {:minutes, 40}))\n```\n\n##### DateTime Struct Comparison Example\n\nNote the example shows a DateTime comparison with utc and std offsets for time zone differences. It is up to the user to be aware of the time zone utc and std offsets.\n\n```\ncontext \"Success with DateTime with utc and std offsets to represent time zone differences\" do\n  let :datetime_pst, do: %DateTime{year: 2017, month: 3, day: 15, hour: 1, minute: 30, second: 30, microsecond: {1, 6}, std_offset: 1*3600, utc_offset: -8*3600, zone_abbr: \"PST\", time_zone: \"America/Los_Angeles\"}\n  let :datetime_est, do: %DateTime{year: 2017, month: 3, day: 15, hour: 6, minute: 30, second: 30, microsecond: {1, 6}, std_offset: 1*3600, utc_offset: -5*3600, zone_abbr: \"EST\", time_zone: \"America/New_York\"}\n\n  it do: expect datetime_pst() |\u003e to(be_close_to(datetime_est(), {:hours, 2}))\nend\n```\n\n### Limitations\n\nMeck has trouble mocking certain modules, such as `erlang`, `os`, and `timer`.\n\nAlso, meck does not track module-local calls. For example, this will not be tracked:\n\n```elixir\ndefmodule SomeModule\n  def some_func, do: another_func()\n\n  def another_func, do: nil\nend\n```\n\nBut this will:\n\n```elixir\ndefmodule SomeModule\n  def some_func, do: __MODULE__.another_func()\n\n  def another_func, do: nil\nend\n```\n\nIt is recommended to prefix module-local calls with `__MODULE__` when using them with meck.\n\nSee [this section in the meck README](https://github.com/eproxus/meck#caveats) for a more detailed explanation.\n\n## Doc specs\nESpec has functionality similar to [`ExUnit.DocTest`](http://elixir-lang.org/docs/stable/ex_unit/).\nRead more about docs syntax [here](http://elixir-lang.org/docs/stable/ex_unit/)\nThe functionality is implemented by two modules:\n`ESpec.DocExample` parses module documentation and `ESpec.DocTest` creates 'spec' examples for it.\n`ESpec.DocExample` functions are just copy-pasted from `ExUnit.Doctest` parsing functionality.\n`ESpec.DocTest` implements `doctest` macro which is identical to `ExUnit`.\n```elixir\ndefmodule DoctestSpec do\n  use ESpec\n  doctest MySuperModule\nend\n```\nThere are three options (similar to `ExUnit.DocTest`):\n\n`:except` - generate specs for all functions except those listed (list of {function, arity} tuples).\n```elixir\ndefmodule DoctestOptionsSpec do\n  use ESpec\n  doctest MySuperModule, except: [fun: 1, func: 2]\nend\n```\n`:only` — generate specs only for functions listed (list of {function, arity} tuples).\n\nAnd `:import` to test a function defined in the module without referring to the module name.Default is `false`. Use this option with care because you can clash with other modules.\n\nThere are three types of specs can be generated based on docs.\n\n- Examples where input and output can be evaluated. For example:\n```elixir\n@doc \"\"\"\niex\u003e Enum.map [1, 2, 3], fn(x) -\u003e\n...\u003e   x * 2\n...\u003e end\n[2,4,6]\n\"\"\"\n```\nSuch examples will be converted to:\n```elixir\nit \"Example description\" do\n  expect input |\u003e to(eq output)\nend\n```\n- Examples which return complex structure so Elixir prints it as `#Name\u003c...\u003e.`:\n```elixir\n@doc \"\"\"\niex\u003e Enum.into([a: 10, b: 20], HashDict.new)\n#HashDict\u003c[b: 20, a: 10]\u003e\n\"\"\"\n```\nThe examples will be converted to:\n```elixir\nit \"Example description\" do\n  expect inspect(input) |\u003e to(eq output)\nend\n```\n- Examples with exceptions:\n```elixir\n@doc \"\"\"\niex(1)\u003e String.to_atom((fn() -\u003e 1 end).())\n** (ArgumentError) argument error\n\"\"\"\n```\nThe examples will be tested as:\n```elixir\nit \"Example description\" do\n  expect fn -\u003e input end |\u003e to(raise_exception error_module, error_message)\nend\n```\n\n## Configuration and options\n```sh\n`MIX_ENV=test mix help espec`\n```\n#### Spec paths and pattern\nYou can change (in the `mix.exs` file) the folder where your specs are and the pattern to match these files.\n```elixir\n def project do\n  ...\n  spec_paths: [\"my_specs\", \"espec\"],\n  spec_pattern: \"*_espec.exs\",\n  ...\n end\n```\n#### Shared spec paths and pattern\nIn addition to specifying the spec paths you can also tell ESpec where to find your SharedSpecs.\n```elixir\n  def project do\n    ...\n    shared_spec_paths: [\"my_specs/shared\", \"espec/my_shared\"],\n    shared_spec_pattern: \"*_shared.exs\",\n    ...\n  end\n```\n#### Coverage\nOne can run specs with coverage:\n```sh\nmix espec --cover\n```\nFind the results in `/cover` folder.\nESpec, like ExUnit, uses very simple wrapper around OTP's cover. But you can override this.\n\nTake a look to [coverex](https://github.com/alfert/coverex) as a perfect example.\n\n#### Stale\n\nSimilar to ExUnit, the `--stale` command line option attempts to run only those test files which reference modules that have changed since the last time you ran this task with `--stale`.\n\nRunning the whole test suite:\n\n```sh\nmix espec --stale\n```\n\nRunning individual file(s):\n\n```sh\nmix espec spec/assertions/be_spec.exs spec/assertions/be_close_to_spec.exs --stale\n```\n\n## Formatters\nThere are three formatters in ESpec:\n- ESpec.Formatters.Doc\n- ESpec.Formatters.Json\n- ESpec.Formatters.Html\n\nThe Doc formatter tries to help you read the failed tests results by doing diffs\nbetween the expected and actual values in some cases (the eq and eql assertions,\nfor example). If you don't want this you can disable it like this:\n```elixir\nESpec.configure fn(config) -\u003e\n  config.formatters [\n    {ESpec.Formatters.Doc, %{diff_enabled?: false}}\n  ]\nend\n```\n\nBy default ESpec uses 'Doc' with empty options.\nIn order to use another one, you must specify formatters in 'ESpec.config'\n\nFor example:\n```elixir\nESpec.configure fn(config) -\u003e\n  config.formatters [\n    {ESpec.Formatters.Json, %{out_path: \"results.json\"}},\n    {ESpec.Formatters.Html, %{out_path: \"results.html\"}},\n    {ESpec.YouCustomFormatter, %{a: 1, b: 2}},\n  ]\nend\n```\n\nESpec design allows custom formatters of test results.\nThe custom formatter is a module which `use ESpec.Formatters.Base` and implement 3 functions:\n- `init/1`\n- `handle_cast/2` for `example_finished` event\n- `handle_cast/2` for `final_result` event\n\nTake a look at `lib/espec/formatters` and `spec_formatters` folders to see examples\n\n#### Other formatters\nThere are community supported formatters:\n- [espec_junit_formatter](https://github.com/mwean/espec_junit_formatter)\n\n## Changelog\n  * 0.2.0:\n    - Basic functionality (contexts, 'before' and 'let', mocking, basic matchers)\n  * 0.3.0:\n     - Tags for examples and contexts\n     - 'config.before' and 'config.finally'\n  * 0.4.0:\n    - Lots of internal changes\n    - Shared examples\n  * 0.5.0:\n    - 'count', 'pid' and 'args' options in 'accepted' assertion\n    - 'async' option for parallel execution\n  * 0.6.0:\n    - String and Dictionary matchers\n    - Doctests\n  * 0.7.0:\n    - Mocking options\n    - Html and Json outputs\n    - capture_io\n  * 0.8.0:\n    - 'only' and 'exclude' options\n    - 'double_underscore' replaced by 'shared'\n  * 1.0.0:\n    - 'let' implementation rewritten completely\n    - 'assert_receive' and 'refute_receive'\n    - 'let_overridable' for shared examples\n    - 'let_ok' and 'let_error'\n    - new syntax with pipe\n  * 1.1.0:\n    - capture_log\n    - 'let' and 'before' with keyword\n  * 1.1.1:\n    - Fix 'finally' execution order\n  * 1.1.2:\n    - Added support for unicode characters in example names\n  * 1.2.0:\n    - before_all and after_all callbacks\n  * 1.2.1:\n    - removed module name duplication in example description\n    - fix statistic output for async examples\n  * 1.2.2:\n    - Elixir 1.4.0 warnings ware fixed\n  * 1.3.0\n    - Formatters were refactored to support custom ones\n  * 1.3.1\n    - Structure diffs were added to 'eq' matcher\n  * 1.3.2\n    - Generated examples were added\n  * 1.3.3\n    - Bug fix structure diff were fixed\n  * 1.3.4\n    - Line number option for contexts\n  * 1.4.0\n    - Pretty diffs for failed specs\n    - 'let' agent fix\n    - 'contain_exactly', 'match_list' and 'change_by' assertions\n    - Elixir 1.2 is no longer supported\n  * 1.4.1\n    - Configurable timeouts for output formatters\n  * 1.4.2\n    - Fix Elixir 1.5.0 issues\n  * 1.4.3\n     - Fix options issues\n  * 1.4.4\n     - Stacktrace for failed examples\n  * 1.4.5\n     - Update 'meck' to fix issue with elrang 20 mocks\n     - Fixed options passing\n  * 1.4.6\n     - Fix doctests (allow \"strings\")\n     - allow keywords in `let_ok` and `let_error`\n     - Fix `before` to ignore not enumerables\n  * 1.5.0\n     - Add `be` and `be_close_to` assertions for `Date`, `Time`, `NaiveDateTime`, and `DateTime`\n     - 'have' matcher for Map\n     - 'let' works for generated examples\n     - 'match_pattern' macro\n  * 1.5.1\n     - Code formatting\n     - Improve `have` and `eq` assertions\n     - Fix `let` caching for shared examples\n  * 1.6.0\n     - Compatibility with OTP 21    \n  * 1.6.1\n     - Doctest fix for Elixir \u003e= 1.7\n  * 1.6.2\n     - Fix 'let' caching in shared specs\n  * 1.6.3\n     - Use 'Task.async_stream' for async examples\n  * 1.6.3\n     - Use 'Task.async_stream' for async examples      \n  * 1.6.4\n     - Run every test in separate process\n  * 1.6.5\n     - Update `meck` dependency   \n  * 1.7.0\n     - Follows deprecation of plural time units (:seconds, :microseconds, etc). Allows only singular.\n     - Remove support of Elixir 1.5.\n  * 1.8.0\n     - Add --stale option.\n     - Remove support of Elixir 1.6.\n  * 1.8.1\n     - Fix compilation warnings.\n  * 1.8.2\n     - Elixir 1.10 compatibility.\n  * 1.8.3\n     - Elixir 1.11 compatibility.\n     - Fails with compilation error if spec can't be compiled\n  * 1.9.0\n    - Erlang 25 and Elixir 1.13 compatibility.\n  * 1.9.1\n    - Simplify espec.init.\n  * 1.9.2\n    - OTP 26 and Elixir 1.16 compatibility. \n    \n## Contributing\n##### Contributions are welcome and appreciated!\n\nRequest a new feature by creating an issue.\n\nCreate a pull request with new features or fixes.\n\nESpec is tested using ExUnit and ESpec. So run:\n```sh\nmix test\nmix espec\n```\n","funding_links":[],"categories":["Testing","Elixir"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantonmi%2Fespec","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fantonmi%2Fespec","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantonmi%2Fespec/lists"}