Packages
espec
1.1.0
1.10.0
1.9.2
1.9.1
1.9.0
1.8.3
1.8.2
1.8.1
1.8.0
1.7.0
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.2.2
1.2.1
1.2.0
1.1.2
1.1.1
1.1.0
1.0.1
1.0.0
0.8.28
0.8.27
0.8.26
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.10
0.8.9
0.8.8
0.8.7
0.8.6
0.8.5
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.1
0.5.0
0.4.2
0.4.1
0.4.0
0.3.7
0.3.5
0.3.0
0.2.0
0.1.0
BDD testing framework for Elixir inspired by RSpec.
Current section
Files
Jump to
Current section
Files
lib/espec/doc_test.ex
defmodule ESpec.DocTest do
@moduledoc """
Implements functionality similar to ExUnit.DocTest.
Provides doctest macro.
Read the ExUnit.DocTest documentation for more info.
There are three types of specs:
:test - examples where input and output can be evaluated:
iex> Enum.map [1, 2, 3], fn(x) ->
...> x * 2
...> end
[2,4,6]
Such examples will be converted to: 'expect(input).to eq(output)' assertion.
:inspect - examples which return complex structure so Elixir prints it as #Name<...>.
iex> Enum.into([a: 10, b: 20], HashDict.new)
#HashDict<[b: 20, a: 10]>
The examples will be converted to: 'expect(inspect input).to eq(output)'.
:error - examples with exceptions:
iex(1)> String.to_atom((fn() -> 1 end).())
** (ArgumentError) argument error
The examples will be converted to: expect(fn -> input end).to raise_exception(error_module, error_message).
"""
@doc "Parses the module and builds 'specs'."
defmacro doctest(module, opts \\ []) do
do_import = Keyword.get(opts, :import, false)
quote do
ESpec.DocTest.__do_doctest__(unquote(module), unquote(opts), unquote(do_import))
end
end
@doc false
defmacro __do_doctest__(module, opts, true) do
quote do
import unquote(module)
ESpec.DocTest.__create_doc_examples__(unquote(module), unquote(opts))
end
end
defmacro __do_doctest__(module, opts, false) do
quote do
ESpec.DocTest.__create_doc_examples__(unquote(module), unquote(opts))
end
end
@doc false
@lint false
defmacro __create_doc_examples__(module, opts) do
quote do
examples = ESpec.DocExample.extract(unquote(module))
examples = if Keyword.get(unquote(opts), :only, :false) do
ESpec.DocTest.__filter_only__(examples, unquote(opts)[:only])
else
examples
end
examples = if Keyword.get(unquote(opts), :except, false) do
ESpec.DocTest.__filter_except__(examples, unquote(opts)[:except])
else
examples
end
Enum.with_index(examples)
|> Enum.reduce({[], {nil, nil}}, fn({ex, index}, {prev_binding, {binding_fun, binding_arity}}) ->
context = Enum.reverse(@context)
{fun, arity} = ex.fun_arity
description = "Doctest for #{unquote(module)}.#{fun}/#{arity} (#{index})"
function = :"#{ESpec.Support.word_chars(description)}_#{index}"
@examples %ESpec.Example{description: description, module: __MODULE__, function: function,
opts: [], file: __ENV__.file, line: __ENV__.line, context: context,
shared: false}
binding = case {binding_fun, binding_arity} do
{^fun, ^arity} -> prev_binding
_ -> []
end
{string_to_eval, new_binding} =
cond do
ex.type == :test ->
{lhs, new_binding} = Code.eval_string(ex.lhs, binding, __ENV__)
{rhs, _} = Code.eval_string(ex.rhs, binding, __ENV__)
str = """
def #{function}(shared) do
shared[:key]
expect(#{inspect lhs}).to eq(#{inspect rhs})
end
"""
{str, new_binding}
ex.type == :error ->
{error_module, error_message} = ex.rhs
lhs = ex.lhs
str = """
def #{function}(shared) do
shared[:key]
expect(fn -> Code.eval_string(#{lhs}) end).to raise_exception(#{error_module}, "#{error_message}")
end
"""
{str, binding}
ex.type == :inspect ->
{lhs, new_binding} = Code.eval_string(ex.lhs, binding, __ENV__)
{rhs, _} = Code.eval_string(ex.rhs, binding, __ENV__)
lhs = inspect(lhs)
str = """
def #{function}(shared) do
shared[:key]
expect(#{inspect lhs}).to eq(#{inspect rhs})
end
"""
{str, new_binding}
true ->
raise RuntimeError, message: "Wrong %ESpec.DocExample{} type!"
end
Code.eval_string(string_to_eval, [], __ENV__)
{binding ++ new_binding, {fun, arity}}
end)
end
end
@doc false
def __filter_only__(examples, list) do
Enum.filter(examples, &Enum.member?(list, &1.fun_arity))
end
@doc false
def __filter_except__(examples, list) do
Enum.filter(examples, fn(ex) ->
!Enum.member?(list, ex.fun_arity)
end)
end
end