Testing and Benchmarking framework for deno π§ββοΈ
Merlin
Merlin is a Jest-inspired testing framework for deno.
Using Matchers
Common Matchers
assertEqual(label: string, config)
assertNotEqual(label: string, config)
evalEquals(testEqual[])
stringContains(label: string, config)
arrayContains(label: string, config)
beNull(label: string, config)
beFalsy(label: string, config)
beTruthy(label: string, config)
All Matchers
assertEqual(label: string, config)
Compare two values and throws an error if the expect and toBe are not equalassertNotEqual(label: string, config)
Compare two values and throws an error if the expect and notBe are equalevalEquals(testEqual[])
evaluate multiple equality tests in an array. If the data is not the same it throws an errorfetchEqual(label: string, config)
evaluate if two values are equal. If the request data is not the same as expected, it throws an errorarrayContains(label: string, config)
evaluates that the array contains an specific data. if the array does not contain the data it throws an errorstringContains(label: string, config)
evaluates if a string contains an specific word. if the string does not contain the word it throws an errorbeNull(label: string, config)
evaluates if a data is nullbeFalsy(label: string, config)
evaluates if a data is a falsy valuebeTruthy(label: string, config)
evaluates if a data is a truthy valueisBigInt(label: string, config)
evaluates if a data is a bigInt value typeisZero(label: string, config)
evaluates if a data is a ZeroisNaN(label: string, config)
evaluates if a data is NaN valuesameLength(label: string, config)
evaluates if data has a specific lengthassertRegExp(label: string, config)
evaluates if a regular expression matchisFunction(label: string, config)
evaluates if a data is a functionisSymbol(label: string, config)
evaluates if a data is a symbolisUndefined(label: string, config)
evaluates if a data is undefinedisString(label: string, config)
evaluates if a data is stringisNumber(label: string, config)
evaluates if a data is numberisEmpty(label: string, config)
evaluates if a data is emptyassertSame(label: string, config)
evaluates if two values are strictly the sameassertGreaterOrEqual(label: string, config)
evaluates whether the expected data is greater than or equal to anotherassertGreater(label: string, config)
evaluates whether the expected data is greater than anotherassertLess(label: string, config)
evaluates if the expected data is less than anotherassertLessOrEqual(label: string, config)
evaluates if the expected data is less than or equal to anotherassertInstanceOf(label: string, config)
evaluates that one object is an instance of anotherassertFloat(label: string, config)
evaluates if two decimal numbers are equalassertThrows(label: string, config)
expect it throws an errorassertThrowsSync(label: string, config)
expect it throws an async errorhaveProperty(label: string, config)
expect an object to contain the properties in its value
Statics
Merlin.Error(msg?: string)
force to throw an errorMerlin.Unimplemented(msg?: string)
Use this to throw a method not implemented errorMerlin.Unreachable()
Use this to throw an Unreachable method error
Install Merlin
install merlin-cli (optional)
deno install --allow-run -n merlin https://deno.land/x/merlin/cli.ts
Mirrors
you can get Merlin from different url.
- from
deno.land/x
import { Merlin } from "https://deno.land/x/merlin/mod.ts";
- from
nest.land
import { Merlin } from "https://x.nest.land/merlin@<version>/mod.ts";
- from
github repo
import { Merlin } from "http://denopkg.com/crewdevio/merlin/mod.ts";
Basic Use
simple assertions.
example.test.ts
import { Merlin } from "https://deno.land/x/merlin/mod.ts";
const test = new Merlin();
test.assertEqual("two plus two is four", {
expect() {
return 2 + 2;
},
toBe() {
return 4;
},
});
run this test in deno.
merlin start
or
deno test
you should see this output on the console.
running 1 tests
test two plus two is four ... ok (17ms)
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (18ms)
Parameters
all assertions have parameters that they can receive, these parameters can change the behavior of the tests.
label
add a description to the test.expect()
this function returns the data and then tests with its matchmaker.toBe()
this function returns the data that we hope is correct.notBe()
this function returns the data that we hope it is incorrect.value()
returns the data expected to be of that type.ignore (optional)
receives a boolean to ignore the test in case the value is true.strict (optional)
receives a boolean, it does a strict comparison of theexpect()
andtoBe()
values.message (optional)
receives a string with the message to display in case the test fails.Ops (optional)
receives a boolean, closes all the operations that never end, for exampleDeno.open("file.txt")
. by default istrue
.Resources (optional)
receives a boolean, terminates all asynchronous processes that interact with the system. by default istrue
.only (optional)
receives a boolean, only tests that haveonly in true
will be executed, the rest will not run.
about resources and ops sanitizers
Certain actions in Deno create resources in the resource table . These resources should be closed after you are done using them.
For each test definition, the test runner checks that all resources created in this test have been closed. This is to prevent resource βleaksβ. This is enabled by default for all tests, but can be disabled by setting the sanitizeResources boolean to false in the test definition.
The same is true for async operation like interacting with the filesystem. The test runner checks that each operation you start in the test is completed before the end of the test. This is enabled by default for all tests, but can be disabled by setting the sanitizeOps boolean to false in the test definition.
async function writeSomething(): Promise<string> {
const decoder = new TextDecoder("utf-8");
Deno.createSync("./texts.txt");
const Package = await Deno.readFileSync("./text.txt");
await Deno.writeTextFile("./text.txt", "test");
return decoder.decode(Package);
}
test.assertEqual("Leak resources test", {
expect: async () => await writeSomething(),
toBe: () => "test",
only: true,
Ops: false,
Resources: false,
});
merlin start
test Leak resources test ... ok (5ms)
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Multiple tests
example.test.ts
test.evalEquals([
{
label: "object assignment",
expect() {
const data: any = { one: 1 };
data["two"] = 2;
return data;
},
toBe() {
return { one: 1, two: 2 };
},
},
{
label: "two plus two is four",
expect() {
return 2 + 2;
},
toBe() {
return 4;
},
},
]);
output
merlin start
running 2 tests
test object assignment ... ok (10ms)
test two plus two is four ... ok (1ms)
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (13ms)
notEqual
example.test.ts
test.assertNotEqual("two plus two not is five", {
expect() {
return 2 + 2;
},
notBe() {
return 4;
},
});
output
merlin start
running 1 tests
test two plus two not is five ... FAILED (2ms)
failures:
two plus two not is five
AssertionError: actual: 4 expected: 4
at assertNotEquals (https://deno.land/std/testing/asserts.ts:195:5)
at fn (merlin.ts:105:9)
at async asyncOpSanitizer ($deno$/testing.ts:34:5)
at async Object.resourceSanitizer [as fn] ($deno$/testing.ts:68:5)
at async TestRunner.[Symbol.asyncIterator] ($deno$/testing.ts:276:11)
at async Object.runTests ($deno$/testing.ts:364:20)
failures:
two plus two not is five
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (2ms)
stringContains
example.test.ts
test.stringContains("hello world contains world", {
Contains: () => "world",
value: () => "Hello World",
});
merlin start
test hello world contains world ... ok (8ms)
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
fetchEqual
test.fetchEqual("fetch data", {
url: "https://jsonplaceholder.typicode.com/todos/1",
type: "json",
toBe() {
return { userId: 1, id: 1, title: "delectus aut autem", completed: false };
},
});
merlin start
test fetch data ... ok (1440ms)
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
testRegExp
test.assertRegExp("regEx match", {
expect: () => "https://google.com",
toBe: () => new RegExp("^https?://[a-z.]+.com$"),
});
merlin start
test regEx match ... ok (6ms)
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (342ms)
Using async code.
you can use asynchronous code by adding async
in expect
, toBe
and value
functions.
example
const test = new Merlin();
test.assertEqual("get error 404", {
async expect() {
const response = await fetch("https://deno.land/std/example/examples.ts");
const data = response.text();
return data;
},
toBe() {
return "404: Not Found";
},
});
Note: all the methods of the merlin class support async function since they have top level await
Create benchmarks using Maven
Maven is a benchmark tool for deno included in Merlin.
Itβs easy to use. example
:
import { Maven } from "https://deno.land/x/merlin/mod.ts";
const benchmark = new Maven();
benchmark.Bench({
name: "Sorting arrays",
fn: () => {
new Array(10000).fill(Math.random()).sort();
},
steps: 1000,
});
benchmark.runBench();
this is the terminal output
Parameters
maven receives the following parameters.
name: string
benchmark namefn(): void
function that contains the codesteps: number
number of times to repeat the benchmark
you can see the details at the end of the benchmark using
import { Maven } from "https://deno.land/x/merlin/mod.ts";
const benchmark = new Maven();
benchmark.Bench({
name: "Sorting arrays",
fn: () => {
new Array(10000).fill(Math.random()).sort();
},
steps: 1000,
});
benchmark.runBench().then(benchmark.Result());
It has a table with the detailed values
ββββββββ Benchmarking finished
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Benchmark name: Sorting array β
βββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ€
β Total runs: 1000 β Total time: 1099.6591 ms β Avg time: 1.0997 ms β
βββββββββββββββββββββββββΌβββββββββββββββββββββ¬ββββββββββ΄ββββββββββββ¬βββββββββββββββββββββββββ€
β min: 0.7768 ms β max: 9.9867 ms β mean: 5.3817 ms β median: 0.8511 ms β
βββββββββββββββββββββββββ΄βββββββββββββββββββββ΄ββββββββββββββββββββββ΄βββββββββββββββββββββββββ€
β Thresholds: 0 ========== 70 ========== 90 ========== β β
βββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β β
β 0.7768 ms _[ 965][96.5%] β======================================================== β
β 2.6188 ms _[ 33][ 3.3%] β== β
β 4.4608 ms _[ 1][ 0.1%] β= β
β 6.3027 ms _[ 0][ 0%] β β
β 8.1447 ms _[ 1][ 0.1%] β= β
β β β
βββββββββββββββββββββββββββββββββ΄ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Contributing
contributions are welcome, create a pull request and send us your feature, first check the CONTRIBUTING GUIDELINES.