# HTTPMock
## Installation
```elixir
def deps do
[
{:httpmock, "~> 0.1.6", only: :test}
]
end
```
### Using
1. Create a router with mocked endpoint in `test/support`
```elixir
defmodule Myapp.APIMockTest do
@moduledoc """
Mock Api
"""
use HTTPMock, behaviour: :httpoison
endpoint "https://jsonplaceholder.typicode.com" do
get "/todos/:id", __MODULE__, :get_todo
get "/users/:id", __MODULE__, :get_user
end
def get_todo(_conn, %{"id" => id}) do
data = ~s({\n \"userId\": #{id},\n \"id\": #{id},\n \"title\": \"delectus aut autem\",\n \"completed\": false\n})
{:ok, %{body: data, status_code: 200}}
end
def get_user(_conn, %{"id" => id}) do
data = ~s({\n "id": #{id},\n "name": "Leanne Graham",\n "username": "Bret",\n "email": "Sincere@april.biz",\n "address": {\n "street": "Kulas Light",\n "suite": "Apt. 556",\n "city": "Gwenborough",\n "zipcode": "92998-3874",\n "geo": {\n "lat": "-37.3159",\n "lng": "81.1496"\n }\n },\n "phone": "1-770-736-8031 x56442",\n "website": "hildegard.org",\n "company": {\n "name": "Romaguera-Crona",\n "catchPhrase": "Multi-layered client-server neural-net",\n "bs": "harness real-time e-markets"\n }\n})
{:ok, %{body: data, status_code: 200}}
end
end
```
### With mimic
2. Using [Mimic](https://github.com/edgurgel/mimic) to mimetize HTTPoison in `test/test_helper.exs`
```elixir
Mimic.copy(HTTPoison)
ExUnit.start()
```
3. use the mimic
```elixir
test "greets the world" do
Mimic.stub_with(HTTPoison, Myapp.APIMockTest)
assert {:ok, %{ status_code: 200}} = HTTPoison.get("https://jsonplaceholder.typicode.com/todos/1")
end
```
### Without mimic
2. Set on top of you module @httpoison const to get env from test.exs
in your file that use httpoison
```elixir
defmodule MyappWeb.Api.Client do
@moduledoc """
Myapp Web Api Client
"""
@httpoison Application.compile_env(:myapp, :httpoison, HTTPoison)
def get_todo(id) do
@httpoison.get("https://jsonplaceholder.typicode.com/todos/#{id}")
## instead of
# HTTPoison.get("https://jsonplaceholder.typicode.com/todos/#{id}")
end
end
```
3. set env in your config/test.exs
```elixir
.
.
.
config :myapp, :httpoison, Myapp.APIMockTest
```
4. Use on test
```elixir
test "greets the world" do
assert {:ok, %{ status_code: 200}} = MyappWeb.Api.Client.get_todos(1)
end
```
### HTTPMock.State
The HTTPMock.State manage the state for your mocked api.
1. Set the state in `test/support/my_state.ex` (my_state.ex is an example of namefile)
```elixir
defmodule Myapp.MyState do
use HTTPMock.State
entity(:todos, default: [], key: :id) ## the field :id is required by now
end
```
The behaviour HTTPMock.state provide functions to manager state of 'table'
- all()
- all(entity)
- one(entity, id)
- create(entity, params)
- delete(entity, id)
- update(entity, id, params)
- reset()
The `reset()` function should be called in setup, before tests, to cleaning state.
### Create a new record
```elixir
:ok = Myapp.MyState.create(:todos, %{id: "uuid", title: "my_title"}) ## the only :id field is required. The other fields, follow your needs
```
I prefer create a new function on Myapp.MyState, because native create function return :ok, and then I want to return the last created record.
```elixir
defmodule Myapp.MyState do
...
def new(params) do
id = "uuid_generated"
create(:todos, %{id: id, title: "my_title"})
one(:todos, id)
end
```
### Get a previous record
with the Id of record
```elixir
returned_element= Myapp.MyState.one(:todos,id) ## or nil
```
### Update a previous record
with the Id of record
```elixir
:ok = Myapp.MyState.update(:todos, id, %{title: "updated title"})
```
### Delete a previous record
with the Id of record
```elixir
:ok = Myapp.MyState.delete(:todos, id)
```
### Get all from table
with the Id of record
```elixir
:ok = Myapp.MyState.all(:todos)
```
### Get all from all state
with the Id of record
```elixir
:ok = Myapp.MyState.all()
```
2. Set the HTTPMock.State on Supervisor on test/test_helper.exs.
We provide the `HTTPMock.State.Supervisor`
in test/test_helper.exs
```elixir
HTTPMock.State.Supervisor.start_link([Myapp.MyState])
```
3. Using in your mocked router or in your tests, (rememeber that apply reset before tests)
in any file, like test/myapp/some_test.exs
```elixir
defmodule Myapp.SomeTest do
...
describe "some test" do
setup do
Myapp.MyState.reset()
end
test "testing something" do
assert :ok = Myapp.MyState.create(:todos, %{id: "uuid", title: "my_title"})
assert {:ok, %{body: json_encoded, status_code: 200 } } = MyappWeb.Api.Client.get_todos(1)
assert %{id: "uuid", title: "my_title"} = Jason.decode!(json_encoded)}
end
end
end
```
## Other way
When using start_supervised, you can set a name for MockState genserver.
```elixir
#test/support/data_case.ex or test/support/conn_case.ex
setup _tags do
mockstate_name = :"id-#{inspect(self())}"
Process.put(:mockstate, mockstate_name) # it's required because the :mockname is getted internal from mockstate genserver
start_supervised!({FxApp.MockState, name: mockstate_name}) # when use start_supervised you don't need anymore reset MockState, because start one for each test
:ok
end
```
For LiveView testing, this second method works fine
```elixir
#test/support/data_case.ex or test/support/conn_case.ex
setup _tags do
mockstate_name = :"id-#{inspect(self())}"
Process.put(:mockstate, mockstate_name) # it's required because the :mockname is getted internal from mockstate genserver
start_supervised!({FxApp.MockState, name: mockstate_name}) # when use start_supervised you don't need anymore reset MockState, because start one for each test
Mimic.stub(HTTPMock.State, :get_name, fn -> mockstate_name end) # stub for each scenario the get_name
:ok
end
in mock route, update to use the state
```elixir
defmodule Myapp.APIMockTest do
@moduledoc """
Mock Api
"""
use HTTPMock, behaviour: :httpoison
endpoint "https://jsonplaceholder.typicode.com" do
get "/todos/:id", __MODULE__, :get_todo
end
def get_todo(_conn, %{"id" => id}) do
returned_element= Myapp.MyState.one(:todos,id)
data = returned_element |> Jason.encode!() ## Example using jason
{:ok, %{body: data, status_code: 200} }
end
...
end
```
That is it.