Page MenuHomePhorge

No OneTemporary

Size
24 KB
Referenced Files
None
Subscribers
None
diff --git a/README.md b/README.md
index 33e676c..6648c73 100644
--- a/README.md
+++ b/README.md
@@ -1,470 +1,473 @@
# Tesla
[![Build Status](https://travis-ci.org/teamon/tesla.svg?branch=master)](https://travis-ci.org/teamon/tesla)
[![Hex.pm](https://img.shields.io/hexpm/v/tesla.svg)](http://hex.pm/packages/tesla)
[![Hex.pm](https://img.shields.io/hexpm/dt/tesla.svg)](https://hex.pm/packages/tesla)
[![Hex.pm](https://img.shields.io/hexpm/dw/tesla.svg)](https://hex.pm/packages/tesla)
[![codecov](https://codecov.io/gh/teamon/tesla/branch/master/graph/badge.svg)](https://codecov.io/gh/teamon/tesla)
[![Inline docs](http://inch-ci.org/github/teamon/tesla.svg)](http://inch-ci.org/github/teamon/tesla)
Tesla is an HTTP client loosely based on [Faraday](https://github.com/lostisland/faraday).
It embraces the concept of middleware when processing the request/response cycle.
> Note that this README refers to the `master` branch of Tesla, not the latest
released version on Hex. See [the documentation](http://hexdocs.pm/tesla) for
the documentation of the version you're using.
## HTTP Client example
Define module with `use Tesla` and choose from a variety of middleware.
```elixir
defmodule GitHub do
use Tesla
plug Tesla.Middleware.BaseUrl, "https://api.github.com"
plug Tesla.Middleware.Headers, %{"Authorization" => "token xyz"}
plug Tesla.Middleware.JSON
def user_repos(login) do
get("/user/" <> login <> "/repos")
end
end
```
Then use it like this:
```elixir
response = GitHub.user_repos("teamon")
response.status # => 200
response.body # => [%{…}, …]
response.headers # => %{'content-type' => 'application/json'}
```
See below for documentation.
## Installation
Add `tesla` as dependency in `mix.exs`
```elixir
defp deps do
[{:tesla, "~> 0.9.0"},
{:poison, ">= 1.0.0"}] # optional, required by JSON middleware
end
```
Also, unless using Elixir `>= 1.4`, add `:tesla` to the `applications` list:
```ex
def application do
[applications: [:tesla, ...], ...]
end
```
## Documentation
- [Middleware](#middleware)
- [Runtime middleware](#runtime-middleware)
- [Adapters](#adapters)
- [Streaming](#streaming)
- [Multipart](#multipart)
- [Testing](#testing)
- [Writing middleware](#writing-middleware)
- [Direct usage](#direct-usage)
- [Cheatsheet](#cheatsheet)
- [Changelog](https://github.com/teamon/tesla/releases)
## Middleware
Tesla is built around the concept of composable middlewares.
This is very similar to how [Plug Router](https://github.com/elixir-plug/plug#the-plug-router) works.
#### Basic
- [`Tesla.Middleware.BaseUrl`](https://hexdocs.pm/tesla/Tesla.Middleware.BaseUrl.html) - set base url
- [`Tesla.Middleware.Headers`](https://hexdocs.pm/tesla/Tesla.Middleware.Headers.html) - set request headers
- [`Tesla.Middleware.Query`](https://hexdocs.pm/tesla/Tesla.Middleware.Query.html) - set query parameters
- [`Tesla.Middleware.Opts`](https://hexdocs.pm/tesla/Tesla.Middleware.Opts.html) - set request options
- [`Tesla.Middleware.FollowRedirects`](https://hexdocs.pm/tesla/Tesla.Middleware.FollowRedirects.html) - follow 3xx redirects
- [`Tesla.Middleware.MethodOverride`](https://hexdocs.pm/tesla/Tesla.Middleware.MethodOverride.html) - set X-Http-Method-Override
- [`Tesla.Middleware.Logger`](https://hexdocs.pm/tesla/Tesla.Middleware.Logger.html) - log requests (method, url, status, time)
- [`Tesla.Middleware.DebugLogger`](https://hexdocs.pm/tesla/Tesla.Middleware.DebugLogger.html) - log full requests & responses
#### Formats
- [`Tesla.Middleware.FormUrlencoded`](https://hexdocs.pm/tesla/Tesla.Middleware.FormUrlencoded.html) - urlencode POST body parameter, useful for POSTing a map/keyword list
- [`Tesla.Middleware.JSON`](https://hexdocs.pm/tesla/Tesla.Middleware.JSON.html) - JSON request/response body
- [`Tesla.Middleware.Compression`](https://hexdocs.pm/tesla/Tesla.Middleware.Compression.html) - gzip & deflate
- [`Tesla.Middleware.DecodeRels`](https://hexdocs.pm/tesla/Tesla.Middleware.DecodeRels.html) - decode `Link` header into `opts[:rels]` field in response
#### Auth
- [`Tesla.Middleware.BasicAuth`](https://hexdocs.pm/tesla/Tesla.Middleware.BasicAuth.html) - HTTP Basic Auth
- [`Tesla.Middleware.DigestAuth`](https://hexdocs.pm/tesla/Tesla.Middleware.DigestAuth.html) - Digest access authentication
#### Error handling
- [`Tesla.Middleware.Timeout`](https://hexdocs.pm/tesla/Tesla.Middleware.Timeout.html) - timeout request after X milliseconds despite of server response
- [`Tesla.Middleware.Retry`](https://hexdocs.pm/tesla/Tesla.Middleware.Retry.html) - retry few times in case of connection refused
- [`Tesla.Middleware.Fuse`](https://hexdocs.pm/tesla/Tesla.Middleware.Fuse.html) - fuse circuit breaker integration
- [`Tesla.Middleware.Tuples`](https://hexdocs.pm/tesla/Tesla.Middleware.Tuples.html) - return `{:ok, env} | {:error, reason}` instead of raising exception
## Runtime middleware
All HTTP functions (`get`, `post`, etc.) can take a dynamic client function as the first parameter.
This allow to use convenient syntax for modifying the behaviour in runtime.
Consider the following case: GitHub API can be accessed using OAuth token authorization.
We can't use `plug Tesla.Middleware.Headers, %{"Authorization" => "token here"}`
since this would be compiled only once and there is no way to insert dynamic user token.
Instead, we can use `Tesla.build_client` to create a dynamic middleware function:
```elixir
defmodule GitHub do
# same as above with a slightly change to `user_repos/1`
def user_repos(client, login) do
# pass `client` argument to `get` function
get(client, "/user/" <> login <> "/repos")
end
def issues(client \\ %Tesla.Client{}) do
# default to empty client that will not include runtime token
get(client, "/issues")
end
# build dynamic client based on runtime arguments
def client(token) do
Tesla.build_client [
{Tesla.Middleware.Headers, %{"Authorization" => "token: " <> token }}
]
end
end
```
and then:
```elixir
client = GitHub.client(user_token)
client |> GitHub.user_repos("teamon")
client |> GitHub.get("/me")
GitHub.issues()
client |> GitHub.issues()
```
The `Tesla.build_client` function can take two arguments: `pre` and `post` middleware.
The first list (`pre`) will be included before any other middleware. In case there is a need
to inject middleware at the end you can pass a second list (`post`). It will be put just
before adapter. In fact, one can even dynamically override the adapter.
For example, a private (per user) cache could be implemented as:
```elixir
def new(user) do
Tesla.build_client [], [
fn env, next ->
case my_private_cache.fetch(user, env) do
{:ok, env} -> env # return cached response
:error -> Tesla.run(env, next) # make real request
end
end
end
end
```
## Adapters
Tesla supports multiple HTTP adapter that do the actual HTTP request processing.
- [`Tesla.Adapter.Httpc`](https://hexdocs.pm/tesla/Tesla.Adapter.Httpc.html) - the default, built-in erlang [httpc](http://erlang.org/doc/man/httpc.html) adapter
- [`Tesla.Adapter.Hackney`](https://hexdocs.pm/tesla/Tesla.Adapter.Hackney.html) - [hackney](https://github.com/benoitc/hackney), "simple HTTP client in Erlang"
- [`Tesla.Adapter.Ibrowse`](https://hexdocs.pm/tesla/Tesla.Adapter.Ibrowse.html) - [ibrowse](https://github.com/cmullaparthi/ibrowse), "Erlang HTTP client"
When using ibrowse or hackney adapters remember to alter applications list in `mix.exs` (for Elixir < 1.4)
```elixir
def application do
[applications: [:tesla, :ibrowse, ...], ...] # or :hackney
end
```
and add it to the dependency list
```elixir
defp deps do
[{:tesla, "~> 0.7.0"},
{:ibrowse, "~> 4.2"}, # or :hackney
{:poison, ">= 1.0.0"}] # for JSON middleware
end
```
## Streaming
If adapter supports it, you can pass a [Stream](http://elixir-lang.org/docs/stable/elixir/Stream.html) as body, e.g.:
```elixir
defmodule ElasticSearch do
use Tesla
plug Tesla.Middleware.BaseUrl, "http://localhost:9200"
plug Tesla.Middleware.JSON
def index(records_stream) do
stream = records_stream |> Stream.map(fn record -> %{index: [some, data]})
post("/_bulk", stream)
end
end
```
Each piece of stream will be encoded as JSON and sent as a new line (conforming to JSON stream format)
## Multipart
You can pass a `Tesla.Multipart` struct as the body.
```elixir
alias Tesla.Multipart
mp =
Multipart.new
|> Multipart.add_content_type_param("charset=utf-8")
|> Multipart.add_field("field1", "foo")
|> Multipart.add_field("field2", "bar", headers: [{:"Content-Id", 1}, {:"Content-Type", "text/plain"}])
|> Multipart.add_file("test/tesla/multipart_test_file.sh")
|> Multipart.add_file("test/tesla/multipart_test_file.sh", name: "foobar")
|> Multipart.add_file_content("sample file content", "sample.txt")
response = MyApiClient.post("http://httpbin.org/post", mp)
```
## Testing
You can set the adapter to `:mock` in tests.
```elixir
# config/test.exs
+# Use mock adapter for all clients
config :tesla, adapter: :mock
+# or only for one
+config :tesla, MyClient, adapter: :mock
```
Then, mock requests before using your client:
```elixir
defmodule MyAppTest do
use ExUnit.Case
setup do
Tesla.Mock.mock fn
%{method: :get, url: "http://example.com/hello"} ->
%Tesla.Env{status: 200, body: "hello"}
%{method: :post, url: "http://example.com/world"} ->
%Tesla.Env{status: 200, body: "hi!"}
end
:ok
end
test "list things" do
assert %Tesla.Env{} = env = MyApp.get("/hello")
assert env.status == 200
assert env.body == "hello"
end
end
```
## Writing middleware
A Tesla middleware is a module with `call/3` function, that at some point calls `Tesla.run(env, next)` to process
the rest of stack.
```elixir
defmodule MyMiddleware do
@behaviour Tesla.Middleware
def call(env, next, options) do
env
|> do_something_with_request
|> Tesla.run(next)
|> do_something_with_response
end
end
```
The arguments are:
- `env` - `Tesla.Env` instance
- `next` - middleware continuation stack; to be executed with `Tesla.run(env, next)`
- `options` - arguments passed during middleware configuration (`plug MyMiddleware, options`)
There is no distinction between request and response middleware, it's all about executing `Tesla.run/2` function at the correct time.
For example, a request logger middleware could be implemented like this:
```elixir
defmodule Tesla.Middleware.RequestLogger do
@behaviour Tesla.Middleware
def call(env, next, _) do
IO.inspect env # print request env
Tesla.run(env, next)
end
end
```
and response logger middleware like this:
```elixir
defmodule Tesla.Middleware.ResponseLogger do
@behaviour Tesla.Middleware
def call(env, next, _) do
res = Tesla.run(env, next)
IO.inspect res # print response env
res
end
end
```
See [built-in middlewares](https://github.com/teamon/tesla/tree/master/lib/tesla/middleware) for more examples.
Middleware should have documentation following this template:
````elixir
defmodule Tesla.Middleware.SomeMiddleware do
@behaviour Tesla.Middleware
@moduledoc """
Short description what it does
Longer description, including e.g. additional dependencies.
### Example usage
```
defmodule MyClient do
use Tesla
plug Tesla.Middleware.SomeMiddleware, most: :common, options: "here"
end
```
### Options
- `:list` - all possible options
- `:with` - their default values
"""
end
````
## Direct usage
You can also use Tesla directly, without creating a client module.
This however won’t include any middleware.
```elixir
# Example get request
response = Tesla.get("http://httpbin.org/ip")
response.status # => 200
response.body # => '{\n "origin": "87.205.72.203"\n}\n'
response.headers # => %{'Content-Type' => 'application/json' ...}
response = Tesla.get("http://httpbin.org/get", query: [a: 1, b: "foo"])
response.url # => "http://httpbin.org/get?a=1&b=foo"
# Example post request
response = Tesla.post("http://httpbin.org/post", "data", headers: %{"Content-Type" => "application/json"})
```
## Cheatsheet
#### Making requests 101
```elixir
# GET /path
get("/path")
# GET /path?a=hi&b[]=1&b[]=2&b[]=3
get("/path", query: [a: "hi", b: [1,2,3]])
# GET with dynamic client
get(client, "/path")
get(client, "/path", query: [page: 3])
# arguments are the same for GET, HEAD, OPTIONS & TRACE
head("/path")
options("/path")
trace("/path")
# POST, PUT, PATCH
post("/path", "some-body-i-used-to-know")
put("/path", "some-body-i-used-to-know", query: [a: "0"])
patch("/path", multipart)
```
#### Configuring HTTP functions visibility
```elixir
# generate only get and post function
use Tesla, only: ~w(get post)a
# generate only delete fuction
use Tesla, only: [:delete]
# generate all functions except delete and options
use Tesla, except: [:delete, :options]
```
#### Disable docs for HTTP functions
```elixir
use Tesla, docs: false
```
#### Decode only JSON response (do not encode request)
```elixir
plug Tesla.Middleware.DecodeJson
```
#### Use other JSON library
```elixir
# use JSX
plug Tesla.Middleware.JSON, engine: JSX, engine_opts: [strict: [:comments]]
# use custom functions
plug Tesla.Middleware.JSON, decode: &JSX.decode/1, encode: &JSX.encode/1
```
#### Custom middleware
```elixir
defmodule Tesla.Middleware.MyCustomMiddleware do
def call(env, next, options) do
env
|> do_something_with_request
|> Tesla.run(next)
|> do_something_with_response
end
end
```
## Contributing
1. Fork it (https://github.com/teamon/tesla/fork)
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
Copyright (c) 2015-2017 [Tymon Tobolski](http://teamon.eu/about/)
diff --git a/config/config.exs b/config/config.exs
index fc491c3..6d90389 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -1,20 +1,22 @@
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config
config :tesla, adapter: :httpc
config :logger, :console,
level: :debug,
format: "$date $time [$level] $metadata$message\n"
if Mix.env == :test do
config :httparrot,
http_port: 8888,
ssl: false,
unix_socket: false
config :sasl,
errlog_type: :error,
sasl_error_logger: false
+
+ config :tesla, MockClient, adapter: :mock
end
diff --git a/lib/tesla/mock.ex b/lib/tesla/mock.ex
index 0c350be..7643f87 100644
--- a/lib/tesla/mock.ex
+++ b/lib/tesla/mock.ex
@@ -1,124 +1,173 @@
defmodule Tesla.Mock do
@moduledoc """
Mock adapter for better testing.
### Setup
```
# config/test.exs
config :tesla, adapter: :mock
# in case MyClient defines specific adapter with `adapter :specific`
config :tesla, MyClient, adapter: :mock
```
### Example test
```
defmodule MyAppTest do
use ExUnit.Case
setup do
Tesla.Mock.mock fn
%{method: :get} ->
%Tesla.Env{status: 200, body: "hello"}
end
:ok
end
test "list things" do
assert %Tesla.Env{} = env = MyApp.get("...")
assert env.status == 200
assert env.body == "hello"
end
end
```
### Setting up mocks
```
# Match on method & url and return whole Tesla.Env
Tesla.Mock.mock fn
%{method: :get, url: "http://example.com/list"} ->
%Tesla.Env{status: 200, body: "hello"}
end
# You can use any logic required
Tesla.Mock.mock fn env ->
case env.url do
"http://example.com/list" ->
%Tesla.Env{status: 200, body: "ok!"}
_ ->
%Tesla.Env{status: 404, body: "NotFound"}
end
# mock will also accept short version of response
# in the form of {status, headers, body}
Tesla.Mock.mock fn
%{method: :post} -> {201, %{}, %{id: 42}}
end
```
+
+ ### Global mocks
+ By default, mocks are bound to the current process,
+ i.e. the process running a single test case.
+ This design allows proper isolation between test cases
+ and make testing in parallel (`async: true`) possible.
+
+ While this style is recommended, there is one drawback:
+ if Tesla client is called from different process then the
+ test case it will not get proper mock.
+
+ To solve this issue it is possible to setup a global mock
+ using `mock_global/1` function.
+
+ ```
+ defmodule MyTest do
+ use ExUnit.Case, async: false # must be false!
+
+ setup_all do
+ Tesla.Mock.mock_global fn
+ env -> # ...
+ end
+
+ :ok
+ end
+
+ # ...
+ end
+ ```
+
+ **WARNING**: Using global mocks may affect tests with local mock
+ (because of fallback to global mock in case local one is not found)
"""
defmodule Error do
defexception env: nil, ex: nil, stacktrace: []
def message(%__MODULE__{ex: nil}) do
"""
There is no mock set for process #{inspect(self())}.
Use Tesla.Mock.mock/1 to mock HTTP requests.
See https://github.com/teamon/tesla#testing
"""
end
def message(%__MODULE__{env: env, ex: %FunctionClauseError{} = ex, stacktrace: stacktrace}) do
"""
Request not mocked
The following request was not mocked:
#{inspect(env, pretty: true)}
#{Exception.format(:error, ex, stacktrace)}
"""
end
end
## PUBLIC API
@doc """
Setup mocks for current test.
+
+ This mock will only be available to the current process.
"""
@spec mock((Tesla.Env.t -> Tesla.Env.t | {integer, map, any})) :: no_return
- def mock(fun) when is_function(fun) do
- Process.put(__MODULE__, fun)
- Agent.start_link(fn -> fun end, name: :__tesla_mock__)
- end
+ def mock(fun) when is_function(fun), do: pdict_set(fun)
+
+ @doc """
+ Setup global mocks.
+
+ **WARNING**: This mock will be available to ALL process.
+ It might cause conflicts when running tests in parallel!
+ """
+ @spec mock_global((Tesla.Env.t -> Tesla.Env.t | {integer, map, any})) :: no_return
+ def mock_global(fun) when is_function(fun), do: agent_set(fun)
+
## ADAPTER IMPLEMENTATION
def call(env, _opts) do
- case {Process.get(__MODULE__), Process.whereis(:__tesla_mock__)} do
- {fun, _pid} when is_function(fun) -> mock_call(fun, env)
- {nil, pid} when is_pid(pid) -> Agent.get(pid, &(mock_call(&1, env)))
- _ -> raise Tesla.Mock.Error, env: env
+ case pdict_get() || agent_get() do
+ nil ->
+ raise Tesla.Mock.Error, env: env
+ fun ->
+ case rescue_call(fun, env) do
+ {status, headers, body} ->
+ %{env | status: status, headers: headers, body: body}
+ %Tesla.Env{} = env ->
+ env
+ end
end
end
- defp mock_call(fun, env) do
- case rescue_call(fun, env) do
- {status, headers, body} ->
- %{env | status: status, headers: headers, body: body}
- %Tesla.Env{} = env ->
- env
+ defp pdict_set(fun), do: Process.put(__MODULE__, fun)
+ defp pdict_get, do: Process.get(__MODULE__)
+
+ defp agent_set(fun), do: {:ok, _pid} = Agent.start_link(fn -> fun end, name: __MODULE__)
+ defp agent_get do
+ case Process.whereis(__MODULE__) do
+ nil -> nil
+ pid -> Agent.get(pid, fn f -> f end)
end
end
defp rescue_call(fun, env) do
fun.(env)
rescue
ex in FunctionClauseError ->
raise Tesla.Mock.Error, env: env, ex: ex, stacktrace: System.stacktrace()
end
end
diff --git a/test/support/mock_client.ex b/test/support/mock_client.ex
new file mode 100644
index 0000000..b0b5f08
--- /dev/null
+++ b/test/support/mock_client.ex
@@ -0,0 +1,18 @@
+defmodule MockClient do
+ use Tesla
+
+ plug Tesla.Middleware.BaseUrl, "http://example.com"
+ plug Tesla.Middleware.JSON
+
+ def list do
+ get("/list")
+ end
+
+ def search() do
+ get("/search")
+ end
+
+ def create(data) do
+ post("/create", data)
+ end
+end
diff --git a/test/tesla/global_mock_test.exs b/test/tesla/global_mock_test.exs
new file mode 100644
index 0000000..08d2729
--- /dev/null
+++ b/test/tesla/global_mock_test.exs
@@ -0,0 +1,19 @@
+defmodule Tesla.GlobalMockTest do
+ use ExUnit.Case, async: false
+
+ setup_all do
+ Tesla.Mock.mock_global fn
+ %{method: :get, url: "http://example.com/list"} -> %Tesla.Env{status: 200, body: "hello"}
+ %{method: :post, url: "http://example.com/create"} -> {201, %{}, %{id: 42}}
+ end
+
+ :ok
+ end
+
+ test "mock request from spawned process" do
+ pid = self()
+ spawn fn -> send pid, MockClient.list() end
+
+ assert_receive %Tesla.Env{status: 200, body: "hello"}
+ end
+end
diff --git a/test/tesla/mock/global_a_test.exs b/test/tesla/mock/global_a_test.exs
new file mode 100644
index 0000000..0984720
--- /dev/null
+++ b/test/tesla/mock/global_a_test.exs
@@ -0,0 +1,16 @@
+defmodule Tesla.Mock.GlobalATest do
+ use ExUnit.Case, async: false
+
+ setup_all do
+ Tesla.Mock.mock_global fn
+ _env -> %Tesla.Env{status: 200, body: "AAA"}
+ end
+
+ :ok
+ end
+
+ test "mock get request" do
+ assert %Tesla.Env{} = env = MockClient.get("/")
+ assert env.body == "AAA"
+ end
+end
diff --git a/test/tesla/mock/global_b_test.exs b/test/tesla/mock/global_b_test.exs
new file mode 100644
index 0000000..32b88df
--- /dev/null
+++ b/test/tesla/mock/global_b_test.exs
@@ -0,0 +1,16 @@
+defmodule Tesla.Mock.GlobalBTest do
+ use ExUnit.Case, async: false
+
+ setup_all do
+ Tesla.Mock.mock_global fn
+ _env -> %Tesla.Env{status: 200, body: "BBB"}
+ end
+
+ :ok
+ end
+
+ test "mock get request" do
+ assert %Tesla.Env{} = env = MockClient.get("/")
+ assert env.body == "BBB"
+ end
+end
diff --git a/test/tesla/mock/local_a_test.exs b/test/tesla/mock/local_a_test.exs
new file mode 100644
index 0000000..1c78786
--- /dev/null
+++ b/test/tesla/mock/local_a_test.exs
@@ -0,0 +1,16 @@
+defmodule Tesla.Mock.LocalATest do
+ use ExUnit.Case, async: true
+
+ setup do
+ Tesla.Mock.mock fn
+ _env -> %Tesla.Env{status: 200, body: "AAA"}
+ end
+
+ :ok
+ end
+
+ test "mock get request" do
+ assert %Tesla.Env{} = env = MockClient.get("/")
+ assert env.body == "AAA"
+ end
+end
diff --git a/test/tesla/mock/local_b_test.exs b/test/tesla/mock/local_b_test.exs
new file mode 100644
index 0000000..31c9586
--- /dev/null
+++ b/test/tesla/mock/local_b_test.exs
@@ -0,0 +1,16 @@
+defmodule Tesla.Mock.LocalBTest do
+ use ExUnit.Case, async: true
+
+ setup do
+ Tesla.Mock.mock fn
+ _env -> %Tesla.Env{status: 200, body: "BBB"}
+ end
+
+ :ok
+ end
+
+ test "mock get request" do
+ assert %Tesla.Env{} = env = MockClient.get("/")
+ assert env.body == "BBB"
+ end
+end
diff --git a/test/tesla/mock_test.exs b/test/tesla/mock_test.exs
index a27cf1b..340dee2 100644
--- a/test/tesla/mock_test.exs
+++ b/test/tesla/mock_test.exs
@@ -1,75 +1,42 @@
defmodule Tesla.MockTest do
use ExUnit.Case
- defp setup_config(_) do
- Application.put_env(:tesla, :adapter, :mock)
- :ok
- end
-
defp setup_mock(_) do
Tesla.Mock.mock fn
%{method: :get, url: "http://example.com/list"} -> %Tesla.Env{status: 200, body: "hello"}
%{method: :post, url: "http://example.com/create"} -> {201, %{}, %{id: 42}}
end
:ok
end
- defmodule Client do
- use Tesla
-
- plug Tesla.Middleware.BaseUrl, "http://example.com"
- plug Tesla.Middleware.JSON
-
- def list do
- get("/list")
- end
-
- def search() do
- get("/search")
- end
-
- def create(data) do
- post("/create", data)
- end
- end
-
describe "with mock" do
- setup [:setup_config, :setup_mock]
+ setup :setup_mock
test "mock get request" do
- assert %Tesla.Env{} = env = Client.list()
+ assert %Tesla.Env{} = env = MockClient.list()
assert env.status == 200
assert env.body == "hello"
end
- test "mock request from spawned process" do
- pid = self()
- spawn fn -> send pid, Client.list() end
-
- assert_receive %Tesla.Env{status: 200, body: "hello"}
- end
-
test "raise on unmocked request" do
assert_raise Tesla.Mock.Error, fn ->
- Client.search()
+ MockClient.search()
end
end
test "mock post request" do
- assert %Tesla.Env{} = env = Client.create(%{"some" => "data"})
+ assert %Tesla.Env{} = env = MockClient.create(%{"some" => "data"})
assert env.status == 201
assert env.body.id == 42
end
end
describe "without mock" do
- setup [:setup_config]
-
test "raise on unmocked request" do
assert_raise Tesla.Mock.Error, fn ->
- Client.search()
+ MockClient.search()
end
end
end
end

File Metadata

Mime Type
text/x-diff
Expires
Tue, Nov 26, 5:16 AM (1 d, 12 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40200
Default Alt Text
(24 KB)

Event Timeline