README.md

[![Build Status](https://travis-ci.org/appunite/mockery.svg?branch=master)](https://travis-ci.org/appunite/mockery)
[![Coverage Status](https://coveralls.io/repos/github/appunite/mockery/badge.svg?branch=master)](https://coveralls.io/github/appunite/mockery?branch=master)
[![GitHub issues](https://img.shields.io/github/issues/appunite/mockery.svg)](https://github.com/appunite/mockery/issues)
[![Hex.pm](https://img.shields.io/hexpm/v/mockery.svg?style=flat)](https://hex.pm/packages/mockery)
[![Hex.pm](https://img.shields.io/hexpm/dt/mockery.svg)](https://hex.pm/packages/mockery)
[![Hex.pm](https://img.shields.io/hexpm/dw/mockery.svg)](https://hex.pm/packages/mockery)

# Mockery

Simple mocking library for asynchronous testing in Elixir.

In test environment it replaces prepared modules by mockable proxy. In other environments your modules remain unchanged.

## Installation

```elixir
def deps do
  [
    {:mockery, "~> 1.0"}
  ]
end
```

## Basic usage

#### Static value mock

```elixir
# prepare tested module
@service Mockery.of(MyApp.UserService)

def all do
  @service.users()
end

def filtered do
  @service.users("filter")
end

# tests
import Mockery

# mock any function :users from MyApp.UserService
mock MyApp.UserService, :users, "mock"
assert all() == "mock"
assert filtered() == "mock"

# mock MyApp.UserService.users/0
mock MyApp.UserService, [users: 0], "mock"
assert all() == "mock"
refute filtered() == "mock"

# mock MyApp.UserService.users/0 with implicit value
mock MyApp.UserService, users: 0
assert all() == :mocked
refute filtered() == :mocked
```

#### Dynamic mock

```elixir
defmodule Foo do
  def bar(value), do: value
end

# prepare tested module
@foo Mockery.of(Foo)

def parse(value) do
  @foo.bar(value)
end

# tests
import Mockery

mock Foo, [bar: 1], fn(value)-> String.upcase(value) end
assert parse("test") == "TEST"
```

## Check if function was called

```elixir
# prepare tested module
@foo Mockery.of(Foo)

def call(value, opts) do
  @foo.bar(value)
end

# tests
import Mockery.Assertions
# use Mockery # when need to import both Mockery and Mockery.Assertions

# assert any function bar from module Foo was called
call(1, %{})
assert_called Foo, :bar

# assert Foo.bar/2 was called
call(1, %{})
assert_called Foo, bar: 2

# assert Foo.bar/2 was called with given args
call(1, %{})
assert_called Foo, :bar, [1, %{}]

# assert Foo.bar/2 was called with 1 as first arg
call(1, %{})
assert_called Foo, :bar, [1, _]

# assert Foo.bar/2 was called with 1 as first arg 5 times
# ...
assert_called Foo, :bar, [1, _], 5

# assert Foo.bar/2 was called with 1 as first arg from 3 to 5 times
# ...
assert_called Foo, :bar, [1, _], 3..5

# assert Foo.bar/2 was called with 1 as first arg 3 or 5 times
# ...
assert_called Foo, :bar, [1, _], [3, 5]
```

#### Refute

Every assert_called/x function/macro has its refute_called/x counterpart.<br>
For more information see [docs](https://hexdocs.pm/mockery/Mockery.Assertions.html)

## Global mock

```elixir
# create helper module
defmodule MyApp.TestUserService do
  use Mockery.Heritage,
    module: MyApp.UserService

  mock [users: 0] do
    [:user1, :user2, :user3]
  end
end

# prepare tested module
defmodule MyApp.UserController do
  @service Mockery.of(MyApp.UserService, by: MyApp.TestUserService)

  def index do
    @service.users()
  end
end

# tests
assert index() == [:user1, :user2, :user3]
```

## Advanced examples

For advanced usage examples see [EXAMPLES.md](EXAMPLES.md)

## Philosophy behind this project

#### Nothing is shared

Mocks and function call history are stored separately for every test in
its own process dictionary.

#### Don't use modules as additional function argument

When you use functions like this:

```elixir
  def something(foo \\ Foo) do
    foo.bar()
  end
```

You loose some compilation warnings. It was always unacceptable for me.
After changing function name I expect that project recompilation will show me
if old name is still used somewhere in my code by throwing `function Foo.bar/0 is
undefined or private` straight into my face. With `Mockery` it's no longer an issue.

#### Don't create custom macros when it's not necessary

To prepare module for mocking we decided to use module attributes instead of
any magic macros that changes code behind the scene.
In our company we believe that any new person joining project should be able
to understand existing code immediately.

## License

Copyright 2017 Tobiasz Małecki <tobiasz.malecki@appunite.com>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.