Expect.Matchers (Expect v3.1.0)
View SourceA matcher is responsible for providing expect/2 three things
- the name for the matcher
- the value that was matched against (optional)
- a function to invoke with the given value to
expect()
Expect will invoke the matcher's function, and if it returns false or an error tuple, it will
construct an error message using the name and the value, and fail the test.
For example, given a matcher that returns %CustomMatcher{name: "end with", expected: "cool", fn: fn given -> String.ends_with?(given, "cool") end},
we could expect the following result
expect("literal fire", to: end_with("cool"))
# raises an error with message "Expected '"literal fire"' to end with '"cool"', but it did not"Alternatively, a matcher that returns %CustomMatcher{name: "be greater than", expected: 5, fn: fn given -> given > 5 end}
would have the following result
expect(0, to: be_greater_than(5))
# raises an error with message "Expected '0' to be greater than '5'"Custom Failure Messages
By default, when a matcher fails, the error message will use the name of the matcher, and the value matched against.
eg: given a matcher returns %CustomMatcher{name: "start with", expected: "gravy", fn: fn given -> String.starts_with?(given, "gravy") end}
we could expect the following line to fail
expect("groovy train", to: start_with("gravy"))
# fails with message "Expected '"groovy train"' to start with '"gravy"'"If you are writing a matcher that doesn't compare the given value against something else, you can omit the actual key
(only the name and fn keys are required)
def start_with_a, do: %CustomMatcher{name: "start with the letter 'A'", fn: fn given -> String.starts_with?(given, "A") end}`
expect("algebra", to: start_with_a())
# passes
expect("monotonic", to: start_with_a())
# fails with message "Expected '"monotonic"' to start with the letter 'a'"Custom error messages
Sometimes, a matcher might receive input that it is incapable of dealing with. A matcher that wants to verify if a string has a certain prefix would be unable to handle input that is not a string. This can be handled by returning an error tuple from a matcher
def end_with(suffix) do: %CustomMatcher{name: "end with", expected: suffix, fn: &verify_suffix(&1, suffix)}
defp verify_suffix(given, suffix) when is_binary(given), do: String.ends_with?(given, suffix)
defp verify_suffix(given, suffix), do: {:error, "end with '#{suffix}', but it was not a string"}
Summary
Functions
Verifies that expected is either the atom :error or an error tuple
Verifies that expected is an empty list, map, or tuple
Verifies that expected is strictly greater than value, using >.
Verifies that expected is strictly less than value, using <.
Verifies that expected is nil
Verifies that expected is a falsy value -- either nil or false
Verifies that the provided value is in the given Enum
Verifies that expected is equal to value, using ==.
Verifies that expected is either a List or String with the given length
Matches expected against the provided regular expression using Regex.match?
Verifies that the given value matches against the given pattern.
Types
Functions
@spec be_an_error() :: t()
Verifies that expected is either the atom :error or an error tuple
@spec be_empty() :: t()
Verifies that expected is an empty list, map, or tuple
Verifies that expected is strictly greater than value, using >.
Verifies that expected is strictly less than value, using <.
@spec be_nil() :: t()
Verifies that expected is nil
@spec be_truthy() :: t()
Verifies that expected is a falsy value -- either nil or false
Verifies that the provided value is in the given Enum
Works with any Enumerable type (eg: Lists, Keyword lists, Ranges, Maps, etc)
If you want to verify that the enumerable ONLY contains the one value then use :only
expect([1], to: contain(only: 1))
Verifies that expected is equal to value, using ==.
If you want to verify strict equality with === then use strict: true
expect(1, to: equal(1.0, strict: true))
@spec have_length(non_neg_integer()) :: t()
Verifies that expected is either a List or String with the given length
Matches expected against the provided regular expression using Regex.match?
Verifies that the given value matches against the given pattern.
expect({:ok, _msg}, to: pattern_match({:ok, "this is fine"}))
expect({:ok, _msg}, to: pattern_match({:error, "this is NOT fine !!!"}))
# raises an error
# you can also pass in variables for the pattern
result = {:error, "oh gods everything is on fire"}
expect({:error, _reason}, to: pattern_match(result))
# nb : you do not want to pass in variables for the given value
# this test will never fail, as it effectively re-assigns the variable `whoops`
whoops = :ok
expect(whoops, to: pattern_match(:error))## Binding variables
You can bind variables during a pattern match and use that for later assertions. This is frequently helpful when you have some large data structure and assert several distinct facts about it
my_data = %{large: %{structure: %{containing: %{a_list: ["cool"]}}}}
expect(%{large: %{structure: %{containing: %{a_list: list}}}}, to: pattern_match(my_data))
expect(list, to: contain("cool"))