Unexpected comes with a type system that is used to explain how different types are compared, diffed, inspected and is also used to limit the scope of assertions.
The following types are provided by out of the box by Unexpected:
Unexpected can be extended with knowledge about new types by calling
addType method with a type definition. The type definition must
implement the required parts of the following interface:
String- the name of the type.
boolean function(value)- a function deciding if the type should be used for the given value.
Note that your type has the option to take precedence over all the built-in
types. Test subjects will be tested against the most recently defined type
identify functions should take care not to break with
null and so on.
String- the name of the base type. Defaults to
boolean function(a, b, equal)- a function capable of comparing two values of this type for equality. If not specified it is inherited from the base type.
function(value, depth, output, inspect)- a function capable of inspecting a value of this type. If not specified it is inherited from the base type.
comparison function(actual, expected, output, diff, inspect)- a function producing a comparison between two values of this type. If not specified it is inherited from the base type.
Adding new types to the system is best explained by an example. Let's
say we wanted to add first class support for a
We start out by creating a basic type for handling
instances. The name of the type should be
Person and it should
inherit from the built-in
object type. Furthermore we add an
identify method that will recognize
When you specify a base type, you inherit the optional members you
didn't implement. In this case we inherited the methods
diff from the
Imagine that we make a failing expectation on a
That is already quite helpful, but it would be even nicer if the
Person instances could read as valid calls to the
constructor. We can fix that by implementing an
inspect method on the type.
Now we get the following output:
That is a bit better, let me explain how it works. The
method is called with the value to be inspected, the depth this type
should be inspected with, an output the inspected value should be
written to, and an inspect function that can be used to recursively
inspect members. The output is an instance of
magicpen extended with a
number of styles.
new Person( without styling, then we append the inspected
name, write a
,, inspect the
age and finish with the closing
inspect is called without a depth parameter it
depth-1. Values inspected with depth zero will be
.... In this case we always want the name so we forward the
same depth to the
Let's say we wanted
Person instances only to be compared by name and not by
age. Then we need to override the
This will produce the same output as above, but that means the diff if wrong. It states that the age should be changed. We can fix that the following way:
diff method just calls the
diff method on the base type
with objects that only contain the name. The
object diff will take
care of all the hard work. We could also have called the
function we got as an argument, but that will go off detecting the
types of the parameters, therefore it is faster to call
on the base directly when you know it is the one you need.
You could also do something really custom as seen below:
That would produce the following output.
This is a rather complicated example and I won't go though the details,
but I would like to comment on the
inline flag. When we diff objects
against each other, the values of the keys will be diffed against each
other. That means diffs are inserted into the containing
structure. You can control this behavior using the
inline flag. If
the child diff is inline, it means that it will be appended directly
into the parent; otherwise the diff will be inserted in an annotation
block. The outputs below shows the contrast between setting the
Person diff to inline or not.
Now that we have implemented a type, we can start adding assertions to it. These assertions will only work on this type or types inheriting from the type.
Person inherits from
object you can use all assertion
object or any of its ancestors. Here is an example:
The best resource for learning more about custom types is to look at how the predefined types are built: