Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add pattern matching examples for assert_called #112

Merged
merged 2 commits into from
Dec 9, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@ See the full [reference documentation](https://hexdocs.pm/mock/Mock.html).
* [Assert called - assert a specific function was called](#Assert-called---assert-a-specific-function-was-called)
* [Assert called - specific value](#Assert-called---specific-value)
* [Assert called - wildcard](#Assert-called---wildcard)
* [Assert called - pattern matching](#Assert-called---pattern-matching)
* [NOT SUPPORTED - Mocking internal function calls](#NOT-SUPPORTED---Mocking-internal-function-calls)
* [Tips](#Tips)
* [Help](#Help)
@@ -284,6 +285,92 @@ defmodule MyTest do
end
````

### Assert called - pattern matching

`assert_called` will check argument equality using `==` semantics, not pattern matching.
For structs, you must provide every property present on the argument as it was called or
it will fail. To use pattern matching (useful when you only care about a few properties on
the argument or need to perform advanced matching like regex matching), provide custom
argument matcher(s) using [`:meck.is/1`](https://hexdocs.pm/meck/meck.html#is-1).

```` elixir
defmodule User do
defstruct [:id, :name, :email]
end

defmodule Network do
def update(%User{} = user), do: # ...
end

defmodule MyTest do
use ExUnit.Case, async: false

import Mock

test "test_name" do
with_mock Network, [update: fn(_user) -> :ok end] do
user = %User{id: 1, name: "Jane Doe", email: "jane.doe@gmail.com"}
Network.update(user)

assert_called Network.update(
:meck.is(fn user ->
assert user.__struct__ = User
assert user.id = 1

# matcher must return true when the match succeeds
true
end)
)
end
end
end
````

You can use any valid Elixir pattern matching/multiple function heads to accomplish
this more succinctly, but remember that the matcher will be executed for _all_ function
calls, so be sure to include a fallback case that returns `false`. For mocked functions
with multiple arguments, you must include a matcher/pattern for each argument.

```` elixir
defmodule Network.V2 do
def update(%User{} = user, changes), do: # ...

def update(id, changes) when is_integer(id), do: # ...

def update(_, _), do: # ...
end

defmodule MyTest do
use ExUnit.Case, async: false

import Mock

test "test_name" do
Network.V2.update(%User{id: 456, name: "Jane Doe"}, %{name: "John Doe"})
Network.V2.update(123, %{name: "John Doe", email: "john.doe@gmail.com"})
Network.V2.update(nil, %{})

# assert that `update` was called with user id 456
assert_called Network.V2.update(
:meck.is(fn
%User{id: 456} -> true
_ -> false
end),
:_
)

# assert that `update` was called with an email change
assert_called Network.V2.update(
:_,
:meck.is(fn
%{email: "john.doe@gmail.com"} -> true
_ -> false
end)
)
end
end
````

## NOT SUPPORTED - Mocking internal function calls

A common issue a lot of developers run into is Mock's lack of support for mocking