Expect
When you're writing tests, you often need to check that values meet certain conditions. Expect gives you access to a number of "matchers" that let you validate different things.
Virtually all matchers below are negatable via prepending .not
after the type (e.g. expect.string(actual).not.toBeEmpty()
, expect.int(actual).not.toBe(42)
). Matchers that are not negatable will be explicitly called out in the documentation below.
expect.array(array('a)).toBeEmpty()
expect.array(array('a)).toContain('a)
expect.array(array('a)).toContainEqual(~equals:('a, 'a) => bool=?, 'a)
expect.array(array('a)).toEqual(~equals:('a, 'a) => bool, array('a))
expect.assertions(int)
expect.hasAssertions()
expect.bool(bool).toBe(bool)
expect.bool(bool).toBeTrue()
expect.bool(bool).toBeFalse()
expect.equal(~equals: ('a, 'a) => bool=?, 'a, 'a)
expect.ext
expect.file(string).toBeEmpty()
expect.file(string).toEqual(string)
expect.file(string).toEqualFile(string)
expect.file(string).toEqualLines(list(string))
expect.file(string).toMatch(string)
expect.file(string).toMatchSnapshot()
expect.float(float).toBeCloseTo(~digits: int=?, float)
expect.fn(unit => 'a).toThrow()
expect.fn(unit => 'a).toThrowException(exn)
expect.int(int).toBe(int)
expect.lines(list(string)).toBeEmpty()
expect.lines(list(string)).toEqual(string)
expect.lines(list(string)).toEqualFile(string)
expect.lines(list(string)).toEqualLines(list(string))
expect.lines(list(string)).toMatch(string)
expect.lines(list(string)).toMatchSnapshot()
expect.list(list('a)).toBeEmpty()
expect.list(list('a)).toContain('a)
expect.list(list('a)).toContainEqual(~equals:('a, 'a) => bool=?, 'a)
expect.list(actual).toEqual(~equals, expected)
expect.mock(mock).lastCalledWith('args)
expect.mock(mock).lastReturnedWith('ret)
expect.mock(mock).nthCalledWith(~equals:('args, 'args) => bool=?, int, 'args)
expect.mock(mock).nthReturnedWith(~equals:('ret, 'ret) => bool=?, int, 'args)
expect.mock(mock).toBeCalled()
expect.mock(mock).toBeCalledTimes(int)
expect.mock(mock).toBeCalledWith(~equals: ('args, 'args) => bool=?, 'args)
expect.mock(mock).toThrow()
expect.mock(mock).toThrowException(exn)
expect.mock(mock).toReturnTimes(int)
expect.mock(mock).toReturnWith(~equals: ('ret, 'ret) => bool=?, 'ret)
expect.option('a).toBe(~equals: ('a, 'a) => bool=?, option('a))
expect.option(option('a)).toBeNone()
expect.option(option('a)).toBeSome()
expect.result(result('a)).toBe(~equalsOk: ('a, 'a) => bool=?, ~equalsError: ('b, 'b) => bool =?, result('a))
expect.result(result('a)).toBeOk()
expect.result(result('a)).toBeError()
expect.same('a, 'a)
expect.notSame('a, 'a)
expect.string(string).toBeEmpty()
expect.string(string).toEqual(string)
expect.string(string).toEqualFile(string)
expect.string(string).toEqualLines(list(string))
expect.string(string).toMatch(string)
expect.string(string).toMatchSnapshot()
expect.array
type equalsFn('a) = ('a, 'a) => bool;
type arrayMatchers('a) = {
toEqual: (~equals: equalsFn('a)=?, array('a)) => unit,
toBeEmpty: unit => unit,
toContain: 'a => unit,
toContainEqual: (~equals: equalsFn('a)=?, 'a) => unit,
};
type arrayMatchersWithNot('a) = {
not: arrayMatchers('a),
toEqual: (~equals: equalsFn('a)=?, array('a)) => unit,
toBeEmpty: unit => unit,
toContain: 'a => unit,
toContainEqual: (~equals: equalsFn('a)=?, 'a) => unit,
};
let expect: {
...
array: 'a. array('a) => arrayMatchersWithNot('a)
...
};
expect.array(array('a)).toBeEmpty()
Use expect.array(expected).toBeEmpty()
to check that an array is empty.
open TestFramework;
describe("expect.array.toBeEmpty", ({test}) => {
test("Empty array should be empty", ({expect}) => {
expect.array([||]).toBeEmpty();
});
});
expect.array(array('a)).toContain('a)
Use expect.array(actual).toContain(expected)
when you want to check that an item is in an array. For testing items in the array, this uses ===
, a physical equality check.
open TestFramework;
describe("Array example", ({test}) => {
test("should contain", ({expect}) => {
expect.array([|1, 2, 3|]).toContain(2);
});
});
expect.array(array('a)).toContainEqual(~equals:('a, 'a) => bool=?, 'a)
Use expect.array(actual).toContainEqual(~equals=?, expected)
when you want to check that an item is in an array. For testing items in the array, this uses ==
a structural equality check.
open TestFramework;
type insect = {
name: string,
numberOfLegs: int,
};
describe("Array example", ({test}) => {
test("contain equal", ({expect}) => {
expect.array(getInsects()).toContainEqual({
name: "centipede",
numberOfLegs: 100,
});
});
});
Comparing types that need a custom comparator (such as floats) can be done using expect.array(actual).toContainEqual(~equals, expected)
.
open TestFramework;
let equals = (f1, f2) => abs_float(f1 -. f2) < 0.1;
describe("Array example", ({test}) => {
test("contain equal", ({expect}) => {
expect.array([|1., 2., 3.|]).toContainEqual(~equals, 0.99);
});
});
expect.array(array('a)).toEqual(~equals:('a, 'a) => bool, array('a))
Use expect.array(actual).toEqual(~equals=?, expected)
to check that an array is equal to another array.
open TestFramework;
describe("Array example", ({test}) => {
test("array equality", ({expect}) => {
expect.array(countToThree()).toEqual([|1, 2, 3|]);
});
});
Comparing arrays of types that need a custom comparator (such as floats) can also be done.
open TestFramework;
let equals = (f1, f2) => abs_float(f1 -. f2) < 0.1;
describe("Array example", ({test}) => {
test("toEqual with custom comparator", ({expect}) => {
expect.array(countToThreeWithFloats()).toEqual(~equals, [|1., 2., 3.|]);
});
});
expect.assertions
(as of Rely 3.1.0)
let expect: {
...
assertions: int => bool,
hasAssertions: unit => unit,
...
};
expect.assertions(int)
expect.assertions(int)
verifies that a certain number of assertions are called during a test. This is often useful when testing code with callbacks, in order to make sure that assertions in a callback actually got called.
open TestFramework;
describe("expect.assertions", ({test}) => {
test("doSomething calls both callbacks", ({expect}) => {
let callback1 = () => expect.bool(true).toBeTrue();
let callback2 = () => expect.bool(true).not.toBeFalse();
doSomething(callback1, callback2);
/* this line can be anywhere inside the function body, if there are multiple
* calls to expect.assertions, the last one takes precedence */
expect.assertions(2);
});
});
expect.hasAssertions()
expect.hasAssertions()
verifies that at least one assertion is called during a test. This is often useful when testing code with callbacks, in order to make sure that assertions in a callback actually got called.
Suppose you have an application that allows for side effects to be dispatched on changes to its state via a callback. We could also use such a feature to test that all state changes in a certain path are valid by making an assertion inside that callback. If we want to do that and also ensure that at least one state change took place, we could write the following code:
open TestFramework;
describe("expect.hasAssertions", ({test}) => {
test("our callback gets called", ({expect}) => {
let validateState = (state) => expect.bool(true).toBeTrue();
App.run(validateState);
/* this line can be anywhere inside the function body, if there are multiple
* calls to expect.assertions, the last one takes precedence */
expect.hasAssertions();
});
});
expect.bool
type boolMatchers = {
toBe: bool => unit,
toBeTrue: unit => unit,
toBeFalse: unit => unit,
};
type boolMatchersWithNot = {
not: boolMatchers,
toBe: bool => unit,
toBeTrue: unit => unit,
toBeFalse: unit => unit,
};
let expect: {
...
bool: bool => boolMatchersWithNot
...
};
expect.bool(bool).toBe(bool)
Use expect.bool(actual).toBe(expected)
to check that a boolean value is equal to a particular boolean value.
For example this can be useful when writing tests inside of a List.map
statement.
open TestFramework;
describe("expect.bool", ({test}) => {
let _ =
List.map(
((testName, food, expected)) =>
test(testName, ({expect}) =>
expect.bool(isDelicious(food)).toBe(expected)
),
[("expect bacon to be delicious", "bacon", true)],
);
();
});
expect.bool(bool).toBeTrue()
Use expect.bool(actual).toBeTrue()
to check that a boolean value is equal to true.
open TestFramework;
describe("expect.bool", ({test}) => {
test("expect bacon to be delicious", ({expect}) => {
expect.bool(isDelicious("bacon")).toBeTrue();
});
});
expect.bool(bool).toBeFalse()
Use expect.bool(actual).toBeFalse()
to check that a boolean value is equal to false.
open TestFramework;
describe("expect.bool", ({test}) => {
test("expect socks not to be delicious", ({expect}) => {
expect.bool(isDelicious("socks")).toBeFalse();
});
});
expect.equal
type equalsFn('a) = ('a, 'a) => bool;
let expect = {
...
equal: (~equals: equalsFn('a), 'a, 'a) => unit,
notEqual: (~equals: equalsFn('a), 'a, 'a) => unit,
...
};
expect.equal(~equals: ('a, 'a) => bool=?, 'a, 'a)
Use expect.equal(~equals=?, expected, actual)
to check that two values are equal using ==
, a structural equality check. The negation of expect.equal
is expect.notEqual
For example to structurally compare two records you could write:
open TestFramework;
type myType = {
string,
int,
};
describe("expect.equal", ({test}) => {
test("should be equal", ({expect}) => {
expect.equal({string: "hello", int: 42}, {string: "hello", int: 42});
});
});
Comparing types that need a custom comparator (such as records that contain floats) can also be done by providing a custom equals function:
open TestFramework;
type myType = {
string,
float,
};
describe("expect.equal", ({test}) => {
test("should be equal", ({expect}) => {
let equals = (t1, t2) =>
t1.string == t2.string && abs_float(t1.float -. t2.float) < 0.001;
expect.equal(
~equals,
{string: "hello", float: 42.},
{string: "hello", float: 41.999999},
);
});
});
expect.ext
let expect('ext): {
...
ext: 'ext
...
};
expect.ext
You can add your own custom matchers to Rely. The matchers you define are available under the ext field of the expect record. Full documentation for writing and using custom matchers can be found here.
expect.file
Use expect.file(filename)
to make assertions about the contents of a file. expect.file
provides the same API as expect.string
and handles the work of reading from the file.
type stringMatchers = {
toBeEmpty: unit => unit,
toEqual: string => unit,
toEqualFile: string => unit,
toEqualLines: list(string) => unit,
toMatch: string => unit,
};
type stringMatchersWithNot = {
not: stringMatchers,
toBeEmpty: unit => unit,
toEqual: string => unit,
toEqualFile: string => unit,
toEqualLines: list(string) => unit,
toMatch: string => unit,
toMatchSnapshot: unit => unit,
};
let expect: {
...
file: string => stringMatchersWithNot,
...
};
expect.file(string).toBeEmpty()
Use expect.file(filename).toBeEmpty
to check that a file is empty.
open TestFramework;
describe("My awesome app", ({test}) => {
test("error log should be empty", ({expect}) => {
MyApp.run();
expect.file("errorLog.txt").toBeEmpty();
});
});
expect.file(string).toEqual(string)
Use expect.file(filename).toEqual(expected)
to check that the contents of a file are equal to some expected value.
open TestFramework;
describe("Spaceballs", ({test}) => {
test("Interrogate Roland", ({expect}) => {
DarkHelmet.interrogate("Roland");
expect.file("password.txt").toEqual("12345");
});
});
expect.file(string).toEqualFile(string)
Use expect.file(actualFilename).toEqualFile(expectedFilename)
to check that the contents of a file are equal to the contents of another file.
open TestFramework;
describe("Spaceballs", ({test}) => {
test("Passwords should match", ({expect}) => {
expect.file("DefenseShield/password.txt").toEqualFile("Skroob/luggagePassword.txt");
});
});
expect.file(string).toEqualLines(list(string))
Use `expect.file(actualFilename).toEqualLines(expectedLines) to check that the contents of a file are equal a list of strings joined by a newline.
For example if you wanted to write a program to generate a particular poem into a file called poem.txt
, you could write:
open TestFramework;
describe("Deep Thought", ({test}) => {
test("should write poetry", ({expect}) => {
let expected = [
"Roses are red",
"Violets are blue",
"Rely is great",
"Not false is true",
];
DeepThought.writeSomePoetry();
expect.file("poem.txt").toEqualLines(expected);
});
});
expect.file(string).toMatch(string)
Use expect.file(actualFilename).toMatch(regex)
to verify that a file matches a PCRE regex.
For example if we want to verify that the file secretRecipe.txt
contains either bacon
or sausage
I could write:
open TestFramework;
describe("My recipe", ({test}) => {
test("should contain a cured pork product", ({expect}) => {
expect.file("secretRecipe.txt").toMatch("(bacon|sausage)");
});
});
expect.file(string).toMatchSnapshot()
This ensures that the file matches the most recent snapshot. To generate a new snapshot or update an old one you can run Rely via the command line and pass the -u
flag.
open TestFramework;
describe("My config generator", ({test}) => {
test("generate default config", ({expect}) => {
ConfigGenerator.generateDefaultConfig("test.json");
expect.file("test.json").toMatchSnapshot();
});
});
Note that there is no
.not.toMatchSnapshot
matcher.
expect.float
type floatMatchers = {toBeCloseTo: (~digits: int=?, float) => unit};
type floatMatchersWithNot = {
toBeCloseTo: (~digits: int=?, float) => unit,
not: floatMatchers,
};
let expect: {
...
float: float => floatMatchersWithNot,
...
};
expect.float(float).toBeCloseTo(~digits: int=?, float)
Using exact equality with floating point numbers doesn't work in general. For example the following test fails:
open TestFramework;
describe("float equality", ({test}) => {
test("using expect.equals will cause this test to fail", ({expect}) => {
expect.equal(0.1 +. 0.2, 0.3);
});
});
This fails because 0.1 +. 0.2
is equal to 0.300000000000000044
in Reason due to the underlying representation of floating point numbers.
Instead, use expect.float(actual).toBeCloseTo(~digits, expected)
. For example, if you want to be sure that 0.2 + 0.1 is equal ot 0.3 with a precision of 5 decimal digits you can use this test:
open TestFramework;
describe("float equality", ({test}) => {
test("should pass with expect.float.toBeCloseTo", ({expect}) => {
expect.float(0.1 +. 0.2).toBeCloseTo(~digits=5, 0.3);
});
});
The optional ~digits
argument has a default value of 2
. The exact criterion used is abs_float(actual -. expected) < (10 ** float_of_int(- digits)) /. 2.0
. With the default value this translates to abs_float(actual -. expected) < 0.005
expect.fn
type negatableFnMatchers = {toThrow: unit => unit};
type fnMatchersWithNot = {
not: negatableFnMatchers,
toThrow: unit => unit,
toThrowException: exn => unit,
};
let expect: {
...
fn: 'a. (unit => 'a) => fnMatchersWithNot
...
};
expect.fn(unit => 'a).toThrow()
Use expect.fn(function).toThrow()
to test that a function throws when it is called. For example, if we want to test that ToothpasteFactory.makeFlavor("duck")
throws an exception because duck flavored toothpaste is too fowl, we could write:
open TestFramework;
describe("ToothpasteFactory", ({test}) => {
test("should throw an exception if asked to make bird flavored toothpaste", ({expect}) => {
expect.fn(() => ToothpasteFactory.makeFlavor("duck")).toThrow();
});
});
expect.fn(unit => 'a).toThrowException(exn)
Use expect.fn(function).toThrowException(exn)
to test that a function throws a specific exception when it is called. For example if we want to test that dividing by zero throws a Division_by_zero
exception we could write:
open TestFramework;
describe("Arithmetic", ({test}) => {
test("should throw Division_by_zero when dividing by zero", ({expect}) => {
expect.fn(() => 1 / 0).toThrowException(Division_by_zero);
});
});
Note that there is no expect.fn(unit => 'a).not.toThrowException(exn). It is encouraged to use expect.fn(unit => 'a).not.toThrow() rather than asserting that a particular exception was not thrown.
expect.int
type intMatchers = {toBe: int => unit};
type intMatchersWithNot = {
not: intMatchers,
toBe: int => unit,
};
let expect: {
...
int: intMatchersWithNot,
...
};
expect.int(int).toBe(int)
Use expect.int(actual).toBe(expected)
to check that an integer is equal to a particular value. For example if you expect Deep Thought to return 42
when asked to calculate the answer to the ultimate question, you could write:
open TestFramework;
describe("expect.int.toBe", ({test}) => {
test("Calculate answer to the ultimate question", ({expect}) => {
let answer = DeepThought.answerUltimateQuestion();
expect.int(answer).toBe(42);
});
});
expect.lines
Use expect.lines(lines)
to make assertions about lines of text. expect.lines
provides the same API as expect.string
and compares against the result of String.concat("\n", lines)
.
type stringMatchers = {
toBeEmpty: unit => unit,
toEqual: string => unit,
toEqualFile: string => unit,
toEqualLines: list(string) => unit,
toMatch: string => unit,
};
type stringMatchersWithNot = {
not: stringMatchers,
toBeEmpty: unit => unit,
toEqual: string => unit,
toEqualFile: string => unit,
toEqualLines: list(string) => unit,
toMatch: string => unit,
toMatchSnapshot: unit => unit,
};
let expect: {
...
lines: list(string) => stringMatchersWithNot,
...
};
expect.lines(list(string)).toBeEmpty()
Use expect.lines(actual).toBeEmpty
to check that a list of lines is empty.
open TestFramework;
describe("Historical movies", ({test}) => {
test("the Charlie Chaplin movei should have no lines", ({expect}) => {
expect.lines(CharlieChaplinMovie.script).toBeEmpty();
});
});
expect.lines(list(string)).toEqual(string)
Use expect.lines(actual).toEqual(expected)
to check that some lines of text are equal to an expected string.
open TestFramework;
describe("Lines", ({test}) => {
test("to equal", ({expect}) => {
let greeting = [
"Hello",
"World"
]
expect.lines(greeting).toEqual("Hello\nWorld");
});
});
expect.lines(list(string)).toEqualFile(string)
Use expect.lines(actual).toEqualFile(filename)
to check that the some lines are equal to the contents of a file.
open TestFramework;
describe("Deep Thought", ({test}) => {
test("should write poetry", ({expect}) => {
expect.lines(DeepThought.writeSomePoetry).toEqualFile("poem.txt");
});
});
expect.lines(list(string)).toEqualLines(list(string))
Use expect.lines(actual).toEqualLines(expected)
to check that some lines are equal to another set of lines.
open TestFramework;
describe("Deep Thought", ({test}) => {
test("should write poetry", ({expect}) => {
let expected = [
"Roses are red",
"Violets are blue",
"Rely is great",
"Not false is true",
];
let actual = DeepThought.writeSomePoetry();
expect.lines(actual).toEqualLines(expected);
});
});
expect.lines(list(string)).toMatch(string)
Use expect.lines(actual).toMatch(regex)
to verify that a file matches a PCRE regex.
For example if we want to verify that the a recipe generator module generates a recipe that contains either bacon
or sausage
I could write:
open TestFramework;
describe("My recipe", ({test}) => {
test("should contain a cured pork product", ({expect}) => {
let recipe = RecipeGenerator.makeRecipe();
expect.lines(recipe).toMatch("(bacon|sausage)");
});
});
expect.lines(list(string)).toMatchSnapshot()
This ensures that the lines matches the most recent snapshot. To generate a new snapshot or update an old one you can run Rely via the command line and pass the -u
flag.
open TestFramework;
describe("My config generator", ({test}) => {
test("generate default config", ({expect}) => {
let actual = ConfigGenerator.generateDefaultConfig();
expect.lines(actual).toMatchSnapshot();
});
});
Note that there is no
.not.toMatchSnapshot
matcher.
expect.list
type equalsFn('a) = ('a, 'a) => bool;
type listMatchers('a) = {
toEqual: (~equals: equalsFn('a)=?, list('a)) => unit,
toBeEmpty: unit => unit,
toContain: 'a => unit,
toContainEqual: (~equals: equalsFn('a)=?, 'a) => unit,
};
type listMatchersWithNot('a) = {
not: listMatchers('a),
toEqual: (~equals: equalsFn('a)=?, list('a)) => unit,
toBeEmpty: unit => unit,
toContain: 'a => unit,
toContainEqual: (~equals: equalsFn('a)=?, 'a) => unit,
};
let expect: {
...
list: 'a. list('a) => listMatchersWithNot('a),
...
};
expect.list(list('a)).toBeEmpty()
Use expect.list(expected).toBeEmpty()
to check that a list is empty.
open TestFramework;
describe("expect.list.toBeEmpty", ({test}) => {
test("Empty list should be empty", ({expect}) => {
expect.list([]).toBeEmpty();
});
});
expect.list(list('a)).toContain('a)
Use expect.list(actual).toContain(expected)
when you want to check that an item is in an list. For testing items in the list, this uses ===
, a physical equality check.
open TestFramework;
describe("list example", ({test}) => {
test("should contain", ({expect}) => {
expect.list([1, 2, 3]).toContain(2);
});
});
expect.list(list('a)).toContainEqual(~equals:('a, 'a) => bool=?, 'a)
Use expect.list(actual).toContainEqual(~equals=-?, 'a)
when you want to check that an item is in an list. For testing items in the list, this uses ==
a structural equality check.
open TestFramework;
type insect = {
name: string,
numberOfLegs: int,
};
describe("list example", ({test}) => {
test("contain equal", ({expect}) => {
expect.list(getInsects()).toContainEqual({
name: "centipede",
numberOfLegs: 100,
});
});
});
Comparing types that need a custom comparator (such as floats) can be done using expect.list(actual).toContainEqual
.
open TestFramework;
let equals = (f1, f2) => abs_float(f1 -. f2) < 0.1;
describe("list example", ({test}) => {
test("contain equal", ({expect}) => {
expect.list([1., 2., 3.]).toContainEqual(~equals, 0.99);
});
});
expect.list(actual).toEqual(~equals, expected)
Use expect.list(actual).toEqual(~equals=?, expected)
to check that an list is equal to another list.
open TestFramework;
describe("list example", ({test}) => {
test("list equality", ({expect}) => {
expect.list(countToThree()).toEqual([1, 2, 3]);
});
});
Comparing lists of types that need a custom comparator (such as floats) can also be done.
open TestFramework;
let equals = (f1, f2) => abs_float(f1 -. f2) < 0.1;
describe("List example", ({test}) => {
test("toEqual with custom comparator", ({expect}) => {
expect.list(countToThreeWithFloats()).toEqual(~equals, [1., 2., 3.]);
});
});
expect.mock
type equalsFn('a) = ('a, 'a) => bool;
type negatableMockMatchers('tupledArgs, 'ret) = {
toThrow: unit => unit,
toBeCalled: unit => unit,
toBeCalledTimes: int => unit,
toBeCalledWith: (~equals: equalsFn('tupledArgs)=?, 'tupledArgs) => unit,
lastCalledWith: (~equals: equalsFn('tupledArgs)=?, 'tupledArgs) => unit,
nthCalledWith: (~equals: equalsFn('tupledArgs)=?, int, 'tupledArgs) => unit,
toReturnTimes: int => unit,
toReturnWith: (~equals: equalsFn('ret)=?, 'ret) => unit,
lastReturnedWith: (~equals: equalsFn('ret)=?, 'ret) => unit,
nthReturnedWith: (~equals: equalsFn('ret)=?, int, 'ret) => unit,
};
type matchersWithNot('tupledArgs, 'ret) = {
not: negatableMockMatchers('tupledArgs, 'ret),
toThrow: unit => unit,
toThrowException: exn => unit,
toBeCalled: unit => unit,
toBeCalledTimes: int => unit,
toBeCalledWith: (~equals: equalsFn('tupledArgs)=?, 'tupledArgs) => unit,
lastCalledWith: (~equals: equalsFn('tupledArgs)=?, 'tupledArgs) => unit,
nthCalledWith: (~equals: equalsFn('tupledArgs)=?, int, 'tupledArgs) => unit,
toReturnTimes: int => unit,
toReturnWith: (~equals: equalsFn('ret)=?, 'ret) => unit,
lastReturnedWith: (~equals: equalsFn('ret)=?, 'ret) => unit,
nthReturnedWith: (~equals: equalsFn('ret)=?, int, 'ret) => unit,
};
let expect: {
...,
mock:
'fn 'ret 'tupledArgs.
Mock.t('fn, 'ret, 'tupledArgs) => matchersWithNot('tupledArgs, 'ret),
...
};
expect.mock(mock).lastCalledWith('args)
Use expect.mock(mock).lastCalledWith(args)
to test what arguments a mock function was last called with.
open TestFramework;
describe("Mock matchers", ({test}) => {
test("lastCalledWith", ({expect}) => {
let mock = Mock.mock1(a => a);
let mockFn = Mock.fn(mock);
let _ = mockFn(1);
let _ = mockFn(2);
expect.mock(mock).lastCalledWith(2);
});
});
expect.mock(mock).lastReturnedWith('ret)
Use expect.mock(mock).lastCalledWith(args)
to test what arguments a mock function was last called with.
open TestFramework;
describe("Mock matchers", ({test}) => {
test("lastCalledWith", ({expect}) => {
let mock = Mock.mock1(n => 2 * n);
let mockFn = Mock.fn(mock);
let _ = mockFn(1);
let _ = mockFn(2);
expect.mock(mock).lastReturnedWith(4);
});
});
expect.mock(mock).nthCalledWith(~equals:('args, 'args) => bool=?, int, 'args)
Use expect.mock(mock).nthCalledWith(~equals=?, n, expected)
to test what arguments a mock function was called with for its nth call. For example suppose that you are implementing map
for some data structure and want to verify that the function is called based on the order elements are added to the structure. You can write:
open TestFramework;
describe("My data structure", ({test}) => {
test("map calls the passed in function in the order elements are added", ({expect}) => {
let double = x => x * 2;
/*there are constructors for up to 7 args */
let mock = Mock.mock1(double);
let myStructure =
MyStructure.empty |> MyStructure.add(10) |> MyStructure.add(50);
let _ = MyStructure.map(Mock.fn(mock), myStructure);
expect.mock(mock).nthCalledWith(1, 10);
expect.mock(mock).nthCalledWith(2, 50);
});
});
For dealing with floats or other situations in which custom equality is required, a custom equals function can be provided:
open TestFramework;
describe("My data structure", ({test}) => {
test("map calls the passed in function in the order elements are added", ({expect}) => {
let double = x => x *. 2.;
let equals = (a, b) => abs_float(a -. b) < 0.1;
let mock = Mock.mock1(double);
let myStructure =
MyStructure.empty |> MyStructure.add(10.) |> MyStructure.add(50.);
let _ = MyStructure.map(Mock.fn(mock), myStructure);
expect.mock(mock).nthCalledWith(~equals, 1, 10.);
expect.mock(mock).nthCalledWith(~equals, 2, 50.);
});
});
expect.mock(mock).nthReturnedWith(~equals:('ret, 'ret) => bool=?, int, 'args)
Use expect.mock(mock).nthReturnedWith(~equals=?, n, expected)
to test the specific value that a mock function returned for the nth call. For example suppose that you are implementing map
for some data structure and want to verify that the function is called based on the order elements are added to the structure. You can write:
open TestFramework;
describe("My data structure", ({test}) => {
test("map calls the passed in function in the order elements are added", ({expect}) => {
let double = x => x * 2;
/*there are constructors for up to 7 args */
let mock = Mock.mock1(double);
let myStructure =
MyStructure.empty |> MyStructure.add(10) |> MyStructure.add(50);
let _ = MyStructure.map(Mock.fn(mock), myStructure);
expect.mock(mock).nthReturnedWith(1, 20);
expect.mock(mock).nthReturnedWith(2, 100);
});
});
For dealing with floats or other situations in which custom equality is required, a custom equals function can be provided:
open TestFramework;
describe("My data structure", ({test}) => {
test("map calls the passed in function in the order elements are added", ({expect}) => {
let double = x => x *. 2.;
let equals = (a, b) => abs_float(a -. b) < 0.1;
let mock = Mock.mock1(double);
let myStructure =
MyStructure.empty |> MyStructure.add(10.) |> MyStructure.add(50.);
let _ = MyStructure.map(Mock.fn(mock), myStructure);
expect.mock(mock).nthReturnedWith(~equals, 1, 20.);
expect.mock(mock).nthReturnedWith(~equals, 2, 100.);
});
});
expect.mock(mock).toBeCalled()
Use expect.mock(mock).toBeCalled
to ensure that a mock function got called.
For example suppose that you are implementing map
for some data structure and want to verify that the function argument to map is actually called on a non empty structure. To test this, you could write:
open TestFramework;
describe("My data structure", ({test}) => {
test("map actually uses the passed in function", ({expect}) => {
let double = x => x * 2;
/*there are constructors for up to 7 args */
let mock = Mock.mock1(double);
let myStructure = MyStructure.empty |> MyStructure.add(1);
let _ = MyStructure.map(Mock.fn(mock), myStructure);
expect.mock(mock).toBeCalled();
});
});
expect.mock(mock).toBeCalledTimes(int)
Use expect.mock(mock).toBeCalledTimes(int)
to ensure that a mock function got called a certain number of times.
For example suppose that you are implementing map
for some data structure and want to verify that the function argument to map is called once per element in the structure. To test this, you could write:
open TestFramework;
describe("My data structure", ({test}) => {
test("map calls the passed in function once per argument", ({expect}) => {
let double = x => x * 2;
/*there are constructors for up to 7 args */
let mock = Mock.mock1(double);
let myStructure =
MyStructure.empty |> MyStructure.add(1) |> MyStructure.add(2);
let _ = MyStructure.map(Mock.fn(mock), myStructure);
expect.mock(mock).toBeCalledTimes(2);
});
});
expect.mock(mock).toBeCalledWith(~equals: ('args, 'args) => bool=?, 'args)
Use expect.mock(mock).toBeCalledWith(~equals=?, args)
to ensure that a mock function got called with particular arguments.
For example suppose that you are implementing map
for some data structure and want to verify that for a data structure with one element, the function passed to map is called with the value of that element.
open TestFramework;
describe("My data structure", ({test}) => {
test("map actually uses the passed in function", ({expect}) => {
let double = x => x * 2;
/*there are constructors for up to 7 args */
let mock = Mock.mock1(double);
let myStructure =
MyStructure.empty |> MyStructure.add(1);
let _ = MyStructure.map(Mock.fn(mock), myStructure);
expect.mock(mock).toBeCalledWith(1);
});
});
For dealing with floats or other situations in which custom equality is required, a custom equals function can be provided:
describe("My data structure", ({test}) => {
test("map actually uses the passed in function", ({expect}) => {
let equals = (f1, f2) => abs_float(f1 -. f2) < 0.1;
let double = x => x *. 2.;
let mock = Mock.mock1(double);
let myStructure =
MyStructure.empty |> MyStructure.add(1.);
let _ = MyStructure.map(Mock.fn(mock), myStructure);
expect.mock(mock).toBeCalledWith(~equals, 1.);
});
});
expect.mock(mock).toThrow()
Use expect.mock(mock).toThrow()
to ensure that the implementation of a mock function has thrown during at least one of its invocations.
For example, if we want to test that our program can successfully handle a callback that raises an error, we could write:
open TestFramework;
exception MyException;
describe("my program", ({test}) => {
test("should gracefully handle an error raised by a callback", ({expect}) => {
let mock = Mock.mock1(_ => raise(MyException));
/* ensure that our program runs without throwing an exception */
expect.fn(() => MyProgram.run(Mock.fn(mock)).not.toThrow();
/* ensure that our callback was called and did throw an exception*/
expect.mock(mock).toThrow();
});
});
expect.mock(mock).toThrowException(exn)
Use expect.mock(mock).toThrowException(exn)
to ensure that the implementation of a mock function has thrown a particular exception during at least one of its invocations.
For example, suppose we have a shipping logistics application that accepts a shipping strategy of type (weight, location) => unit
and we want to verify that our byCarrierPigeon
strategy throws a TooHeavy
exception when asked to ship a grand piano.
open TestFramework;
describe("My Shipping app", ({test}) => {
test("can't ship a grand piano by carrier pigeon", ({expect}) => {
let mock = Mock.mock2(ShippingStrategies.byCarrierPigeon);
MyShippingApp.ship(Items.grandPiano, Mock.fn(mock));
expect.mock(mock).toThrowException(TooHeavy);
});
});
Note that there is no expect.mock(mock).not.toThrowException(exn). It is encouraged to use expect.mock(mock).not.toThrow() rather than asserting that a particular exception was not thrown.
expect.mock(mock).toReturnTimes(int)
Use expect.mock(mock).toReturnTimes(numTimes)
to ensure that a mock function returned successfully (i.e. did not throw an error) an exact number of times. Any calls to the mock function that throw an exception are not counted toward the number of times the function returned.
open TestFramework;
describe("expect.mock.toReturnTimes", ({test}) => {
test("Mock returns twice", ({expect}) => {
let mock = Mock.mock1(_ => ());
let mockFunction = Mock.fn(mock);
let _ = mockFunction();
let _ = mockFunction();
expect.mock(mock).toReturnTimes(2);
});
});
expect.mock(mock).toReturnWith(~equals: ('ret, 'ret) => bool=?, 'ret)
Use expect.mock(mock).toReturnWith(~equals=?, expected)
to ensure that a mock function has returned at least once with a specific value.
For example suppose that you are implementing map
for some data structure and want to verify that when passing the function double
to map
on a structure containing 1
, 2
will be returned.
open TestFramework;
describe("My data structure", ({test}) => {
test("map actually uses the passed in function", ({expect}) => {
let double = x => x * 2;
/*there are constructors for up to 7 args */
let mock = Mock.mock1(double);
let myStructure = MyStructure.empty |> MyStructure.add(1);
let _ = MyStructure.map(Mock.fn(mock), myStructure);
expect.mock(mock).toReturnWith(2);
});
});
For dealing with floats or other situations in which custom equality is required, a custom equals function can be provided:
describe("My data structure", ({test}) => {
test("map actually uses the passed in function", ({expect}) => {
let equals = (f1, f2) => abs_float(f1 -. f2) < 0.1;
let double = x => x *. 2.;
let mock = Mock.mock1(double);
let myStructure =
MyStructure.empty |> MyStructure.add(1.);
let _ = MyStructure.map(Mock.fn(mock), myStructure);
expect.mock(mock).toReturnWith(~equals, 2.);
});
});
expect.option
type equalsFn('a) = ('a, 'a) => bool;
type optionMatchers('a) = {
toBeNone: unit => unit,
toBeSome: unit => unit,
toBe: (~equals: equalsFn('a)=?, option('a)) => unit,
};
type optionMatchersWithNot('a) = {
not: optionMatchers('a),
toBeNone: unit => unit,
toBeSome: unit => unit,
toBe: (~equals: equalsFn('a)=?, option('a)) => unit,
};
let expect: {
...
option: 'a. option('a) => optionMatchersWithNot('a),
...
};
expect.option('a).toBe(~equals: ('a, 'a) => bool=?, option('a))
Use expect.option(actual).toBe(~equals=?, expected)
to test the value of an option.
open TestFramework;
describe("expect.option.toBe", ({test}) => {
test("Deep thought should compute the ultimate answer", ({expect}) => {
let answer = DeepThought.answerUltimateQuestion();
expect.option(answer).toBe(Some(42));
});
test("Custom equality", ({expect}) => {
let equals = (f1, f2) => f1 -. f2 < 0.001;
expect.option(Some(1.)).toBe(~equals, Some(1.000001));
});
});
expect.option(option('a)).toBeNone()
Use expect.option(actual).toBeNone()
to test that the value of an option is equal to None
;
open TestFramework;
describe("expect.option.toBeNone", ({test}) => {
test("shouldn't be anything better than bacon for breakfast", ({expect}) => {
let result = BreakfastFoodRanker.findSuperiorOptionTo("bacon");
expect.option(result).toBeNone();
});
});
expect.option(option('a)).toBeSome()
Use expect.option(actual).toBeSome()
to test that the value of an option is not equal to None
. This is equivalent to expect.option(actual).not.toBeNone()
.
open TestFramework;
describe("expect.option.toBeNone", ({test}) => {
test("should be something better than octopus for breakfast", ({expect}) => {
let result = BreakfastFoodRanker.findSuperiorOptionTo("octopus");
expect.option(result).toBeSome();
});
});
expect.result
type equalsFn('a) = ('a, 'a) => bool;
type resultMatchers('a, 'b) = {
toBeOk: unit => unit,
toBeError: unit => unit,
toBe:
(
~equalsOk: equalsFn('a)=?,
~equalsError: equalsFn('b)=?,
result('a, 'b)
) =>
unit,
};
type resultMatchersWithNot('a, 'b) = {
not: resultMatchers('a, 'b),
toBeOk: unit => unit,
toBeError: unit => unit,
toBe:
(
~equalsOk: equalsFn('a)=?,
~equalsError: equalsFn('b)=?,
result('a, 'b)
) =>
unit,
};
let expect: {
...
result: 'a 'b. result('a, 'b) => resultMatchersWithNot('a, 'b),
...
};
expect.result(result('a)).toBe(~equalsOk: ('a, 'a) => bool=?, ~equalsError: ('b, 'b) => bool =?, result('a))
Use expect.result(actual).toBe(~equalsOk=?, ~equalsError=?, expected)
to test the value of a result. The optional ~equalsOk
and ~equalsError
can be used when comparing values for which the default ==
is insufficient, such as with floats.
open TestFramework;
describe("expect.result.toBe", ({test}) => {
test("Deep thought should compute the ultimate answer", ({expect}) => {
let answer = DeepThought.answerUltimateQuestion();
expect.option(answer).toBe(Ok(42));
});
test("Custom equality", ({expect}) => {
let equals = (f1, f2) => f1 -. f2 < 0.001;
expect.option(Some(1.)).toBe(~equalsOk=equals, Some(1.000001));
expect.option(Error(1.)).toBe(~equalsError=equals, Error(1.000001));
});
});
expect.result(result('a)).toBeOk()
Use expect.option(actual).toBeNone()
to test that the value of a result matches the Ok
constructor.
open TestFramework;
describe("expect.option.toBeOk", ({test}) => {
test("Car should be able to find directions to Disney World", ({expect}) => {
let result = Car.navigate("Disney World");
expect.option(result).toBeOk();
});
});
expect.result(result('a)).toBeError()
Use expect.option(actual).toBeNone()
to test that the value of a result matches the Error
constructor.
open TestFramework;
describe("expect.option.toBeError", ({test}) => {
test("Car should not be able to find directions to the moon", ({expect}) => {
let result = Car.navigate("the moon");
expect.option(result).toBeError();
});
});
expect.same
let expect: {
...
same: 'a. ('a, 'a) => unit,
notSame: 'a. ('a, 'a) => unit,
...
};
expect.same('a, 'a)
Use expect.same(expected, actual)
to check that two values are equal using ===
, a physical equality check.
expect.notSame('a, 'a)
Use expect.notSame(expected, actual)
to check that two values are not equal using ===
, a physical equality check.
open TestFramework;
type person = {
name: string,
age: int,
};
describe("expect.notSame", ({test}) => {
test("two different people with the same name and age should not be the same", ({expect}) => {
expect.notSame(
{name: "Alan Turing", age: 41},
{name: "Alan Turing", age: 41},
);
});
});
expect.string
type stringMatchers = {
toBeEmpty: unit => unit,
toEqual: string => unit,
toEqualFile: string => unit,
toEqualLines: list(string) => unit,
toMatch: string => unit,
};
type stringMatchersWithNot = {
not: stringMatchers,
toBeEmpty: unit => unit,
toEqual: string => unit,
toEqualFile: string => unit,
toEqualLines: list(string) => unit,
toMatch: string => unit,
toMatchSnapshot: unit => unit,
};
let expect: {
...
string: string => stringMatchersWithNot,
...
};
expect.string(string).toBeEmpty()
Use expect.string(actual).toBeEmpty()
to check that a string is empty.
open TestFramework;
describe("expect.string.toBeEmpty", ({test}) => {
test("empty string should be empty", ({expect}) => {
expect.string("").toBeEmpty();
});
});
expect.string(string).toEqual(string)
Use expect.string(actual).toEqual(expected)
to test that a string is equal to a particular value.
open TestFramework;
describe("Greeter", ({test}) => {
test("greeting should be Hello World!", ({expect}) => {
expect.string(Greeter.greeting).toEqual("Hello World!");
});
});
expect.string(string).toEqualFile(string)
Use expect.string(actual).toEqualFile(filename)
to check that a string is equal to the contents of a file.
open TestFramework;
describe("Spaceballs", ({test}) => {
test("Password should be 12345", ({expect}) => {
expect.string("12345").toEqualFile("DefenseShield/password.txt");
});
});
expect.string(string).toEqualLines(list(string))
Use expect.string(actual).toEqualLines(expected)
to check that a string is equal to a list of strings joined by a new line.
open TestFramework;
describe("Deep Thought", ({test}) => {
test("should write poetry", ({expect}) => {
let expected = [
"Roses are red",
"Violets are blue",
"Rely is great",
"Not false is true",
];
let actual = DeepThought.writeSomePoetry();
expect.string(actual).toEqualLines(expected);
});
});
expect.string(string).toMatch(string)
Use expect.string(actual).toMatch(regex)
to verify that a string matches a PCRE regex.
For example if we want to verify that the a recipe generator module generates a recipe that contains either bacon
or sausage
I could write:
open TestFramework;
describe("My recipe", ({test}) => {
test("should contain a cured pork product", ({expect}) => {
let recipe = RecipeGenerator.makeRecipe();
expect.string(recipe).toMatch("(bacon|sausage)");
});
});
expect.string(string).toMatchSnapshot()
This ensures that the string matches the most recent snapshot. To generate a new snapshot or update an old one you can run Rely via the command line and pass the -u
flag.
open TestFramework;
describe("My config generator", ({test}) => {
test("generate default config", ({expect}) => {
let actual = ConfigGenerator.generateDefaultConfig();
expect.string(actual).toMatchSnapshot();
});
});
Note that there is no
.not.toMatchSnapshot
matcher.