Last active
June 6, 2019 10:00
-
-
Save nyaray/145749564603281e860a735ccd47bb26 to your computer and use it in GitHub Desktop.
Specification checking thing for verifying interactions with mocks.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule SpecCheck do | |
defmodule MockInteractionException do | |
@moduledoc """ | |
A structured representation of one of: | |
- unmet expectation | |
- missing interactions | |
- unexpected interactions | |
""" | |
defexception message: "Interaction with mock did not match expectation.", | |
description: nil, | |
issue: nil, | |
line: 0 | |
def unmet(reason, line) do | |
%MockInteractionException{ | |
description: "Wrong interaction", | |
issue: {:unmet, {line, reason}} | |
} | |
end | |
def expected(spec, line) do | |
%MockInteractionException{ | |
description: "Missing interactions", | |
issue: {:expected, spec}, | |
line: line | |
} | |
end | |
def unexpected(actual, line) do | |
%MockInteractionException{ | |
description: "Unexpected interactions", | |
issue: {:unexpected, actual}, | |
line: line | |
} | |
end | |
end | |
@doc """ | |
check/2-3 will report when `actual` does not conform to `spec` when given | |
- `spec`, a poor man's match spec, and | |
- `actual`, an arbitrary term | |
Both `spec` and `actual` are expected to be lists | |
NOTE: lines defaults to `1` because the interactions are numbered for humans. | |
""" | |
def check(spec, actual, lines \\ 1) do | |
case {spec, actual} do | |
{[], []} -> | |
{:ok, lines} | |
{spec, []} -> | |
throw(MockInteractionException.expected(spec, lines)) | |
{[], actual} -> | |
throw(MockInteractionException.unexpected(actual, lines)) | |
{[s | spec_rest], [a | actual_rest]} -> | |
case check_step(s, a) do | |
:cont -> check(spec_rest, actual_rest, lines + 1) | |
{:halt, reason} -> throw(MockInteractionException.unmet(reason, lines)) | |
end | |
end | |
end | |
# | |
# pairwise element check of the respective heads of spec and actual | |
# | |
# check "don't care" | |
defp check_step(:_, _), do: :cont | |
# check bitstrings | |
defp check_step(:bitstring, b) when is_bitstring(b), do: :cont | |
defp check_step(:bitstring, bad), do: mismatch(:bitstring, bad) | |
# check keys in tuples | |
defp check_step({:_, sv}, {_ak, av}), do: check_step(sv, av) | |
defp check_step({:bitstring, sv}, {b, av}) when is_bitstring(b), do: check_step(sv, av) | |
defp check_step({spec_key, sv}, {spec_key, av}), do: check_step(sv, av) | |
defp check_step({spec_key, _sv}, {bad_key, _av}), do: mismatch(spec_key, bad_key) | |
# check exact match, last resort | |
defp check_step(term, term), do: :cont | |
defp check_step(term, bad), do: mismatch(term, bad) | |
# utility | |
defp mismatch(spec, actual), do: {:halt, %{expected: spec, actual: actual}} | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment