{"id":13509231,"url":"https://github.com/Driftrock/mockingbird","last_synced_at":"2025-03-30T13:31:47.557Z","repository":{"id":62429890,"uuid":"87812072","full_name":"Driftrock/mockingbird","owner":"Driftrock","description":"A set of helpers to create http-aware modules that are easy to test. ","archived":false,"fork":false,"pushed_at":"2018-05-03T13:05:16.000Z","size":37,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-21T23:48:24.475Z","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":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Driftrock.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":"2017-04-10T13:08:36.000Z","updated_at":"2019-07-30T19:24:33.000Z","dependencies_parsed_at":"2022-11-01T20:04:18.749Z","dependency_job_id":null,"html_url":"https://github.com/Driftrock/mockingbird","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Driftrock%2Fmockingbird","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Driftrock%2Fmockingbird/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Driftrock%2Fmockingbird/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Driftrock%2Fmockingbird/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Driftrock","download_url":"https://codeload.github.com/Driftrock/mockingbird/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246323941,"owners_count":20759054,"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.923Z","updated_at":"2025-03-30T13:31:47.014Z","avatar_url":"https://github.com/Driftrock.png","language":"Elixir","funding_links":[],"categories":["Testing"],"sub_categories":[],"readme":"# Mockingbird\n\n[![Build Status](https://travis-ci.org/Driftrock/mockingbird.svg?branch=master)](https://travis-ci.org/Driftrock/mockingbird)\n\n[![Inline docs](http://inch-ci.org/github/Driftrock/mockingbird.svg)](http://inch-ci.org/github/Driftrock/mockingbird)\n\nMockingbird helps you create API consumers that are easy to test.\n\n## Why use Mockingbird?\n\nAt Driftrock we have lots of small applications that communicate with other external services, these could be other Driftrock APIs or third-parties APIs. When we're working on an application we don't want to have to run and rely on all possible external services to validate changes as this dramatically slows down the development process. Instead we would much rather change, test and deploy each application independently, safe in the knowledge that integrates nicely with other services.\n\nThe prevalent solution in the Elixir community for this type of testing is to [switch external client modules for fake implementations depending on the environment](http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/). This solution is simple and works nicely, however we found that our tests were not exercising enough of the production external client to provide us with the safety we needed when making changes. It's too easy to fall into the trap of only making changes to the test client and not replicating those in the production client. So we decided to drop one level deeper and switch our HTTP Client (typically `HTTPPoison`) for a fake implementation when testing. Mockingbird is our solution for that.\n\n## Installation\n\nAdd `mockingbird` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    # ...\n    {:mockingbird, \"~\u003e 0.2.0\"},\n    # ...\n  ]\nend\n```\n\n## Usage\n\n```elixir\n# lib/my_app/github.ex\ndefmodule MyApp.Github do\n  use Mockingbird, test: MyApp.MockGithubHttpClient\n\n  def get_account_info(username) do\n    http_client().call(:get, \"https://api.github.com/users/\" \u003c\u003e username)\n  end\nend\n\n# test/support/mock_github_http_client.ex\ndefmodule MyApp.MockGithubHttpClient do\n  import Mockingbird.Client\n\n  # Define `call` methods for each `call` head (ie. verb, url, params) you\n  # want to mock for tests\n  # `respond` helper method returns struct mimicking HTTPoison.Response\n  def call(:get, \"https://api.github.com/users/amencarini\") do\n    respond :ok, 200, \"\"\"\n    {\n      \"login\": \"amencarini\",\n      \"id\": 1100003\n    }\n    \"\"\"\n  end\nend\n\n# test/my_app/git_test.exs\ndefmodule MyApp.GithubTest do\n  use ExUnit.Case\n\n  describe \"MyApp.Github.get_account_info/1\" do\n    test \"it returns data for the selected user\" do\n      {:ok, res} = MyApp.Github.get_account_info(\"amencarini\")\n      assert Poison.decode(res.body) == %{\"login\" =\u003e \"amencarini\", \"id\" =\u003e 1100003}\n    end\n  end\nend\n```\n\n### Fallback on live client\n\nSometimes you might want to fallback on the live client inside tests (e.g.: you have some\ntests running against the live API you're consuming.) To do so, wrap your test\nin a `with_client(environment)` call:\n\n```elixir\n# test/my_app/github_test.exs\ndefmodule MyApp.GithubTest do\n  use ExUnit.Case\n\n  describe \"MyApp.Github.get_account_info/1\" do\n    test \"checks the real API hasn't changed\" do\n      require  MyApp.Github # Needed to get the `with_client` macro available\n\n      MyApp.Github.with_client(:prod) do\n        {:ok, res} = MyApp.Github.get_account_info(\"amencarini\")\n        assert Poison.decode(res.body) == %{\"login\" =\u003e \"amencarini\", \"id\" =\u003e 1100003}\n      end\n    end\n  end\nend\n```\n\n## Configuration\n\nMockingbird uses HTTPoison as default for HTTP calls when key for current\nenvironment is not set in `use`. You can create or customise your default client. You can either specify this globally at config level:\n\n```elixir\n# config/config.exs\nconfig :mockingbird,\n  default_client: MyApp.RealHttpClient\n```\n\nOr on a consumer basis for specific environments:\n\n```elixir\n# lib/my_app/github.ex\ndefmodule MyApp.Github do\n  use Mockingbird,\n    test: MyApp.MockGithubHttpClient\n    prod: MyApp.RealHttpClient\n\n  def get_account_info(username) do\n    http_client().call(:get, \"https://api.github.com/users/\" \u003c\u003e username)\n  end\nend\n```\n\nYour live client just needs to implement a `call` function that pattern matches\non http verb, url, params and headers.\n\n```elixir\n# lib/my_app/real_http_client.ex\ndefmodule MyApp.RealHttpClient do\n  def call(verb, url, params, headers) do\n    # Do your magic here\n  end\nend\n```\n\nIn fact client interface is the same for live and test clients. It is only\nconvenient to have default client for live and on the other hand have few helpers\nin test clients.\n\n### Clients per environment\n\nYou might want to set different clients per different environments. To do so you\ncan setup your consumer with a list of clients to use. Keys matches with current\n`Mix.env`.\n\n```elixir\n# lib/my_app/github.ex\ndefmodule MyApp.Github do\n  use Mockingbird,\n    test: MyApp.MockGithubHttpClient,\n    staging: MyApp.StagingHttpClient\n\n  def get_account_info(username) do\n    http_client().call(:get, \"https://api.github.com/users/\" \u003c\u003e username)\n  end\nend\n```\n\nYou can achieve the same by pointing to a `Mix.config` item. If no configuration\nis found Mockingbird will fallback on the live client.\n\n```elixir\n# config/test.exs\nconfig :my_app,\n  github_http_client: MyApp.MockGithubHttpClient\n\n# lib/my_app/github.ex\ndefmodule MyApp.Github do\n  use Mockingbird, client: Application.get_env(:my_app, :github_http_client)\n\n  def get_account_info(username) do\n    # This will use `MyApp.GitMockHttpClient` on test, and the real http client\n    # in all other environments.\n    http_client().call(:get, \"https://api.github.com/users/\" \u003c\u003e username)\n  end\nend\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDriftrock%2Fmockingbird","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FDriftrock%2Fmockingbird","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDriftrock%2Fmockingbird/lists"}