A comprehensive AssemblyScript testing solution, offering developers a robust suite of features to ensure their code performs as expected:
- Function Mocking
- Coverage statistics
- Expectations
Install Assemblyscript Unittest Framework using npm
npm install --save-dev assemblyscript-unittest-framework
Let's get started by writing a test for a simple function that add two numbers. Assume that there is already environment of assemblyscript.
First, create source/sum.ts
:
export function add(a: i32, b: i32): i32 {
return a + b;
}
Then, create a file named tests/sum.test.ts
. This will contain our actual test:
import { test, expect, endTest } from "assemblyscript-unittest-framework/assembly";
import { add } from "../source/sum";
test("sum", () => {
expect(add(1, 2)).equal(3);
expect(add(1, 1)).equal(3);
});
endTest(); // Don't forget it!
Don't forget endTest()
at the end of *.test.ts
files
Create a config file in project root as-test.config.js
:
for cjs:
module.exports = {
include: ["source", "tests"],
};
for mjs:
export default {
include: ["source", "tests"],
};
Add the following section to your package.json
{
"scripts": {
"test": "as-test"
}
}
Finally, run npm run test
and as-test will print this message:
transform source/sum.ts => build/source/sum.ts.cov
transform build/source/sum.ts.cov => build/source/sum.ts
transform tests/sum.test.ts => build/tests/sum.test.ts
(node:489815) ExperimentalWarning: WASI is an experimental feature. This feature could change at any time
test case: 1/2 (success/total)
Error Message:
sum:
tests/sum.test.ts:6:3 (6:3, 6:29)
You can also use npx as-test -h
for more information to control detail configurations
This is the template of as-test.config.js
:
module.exports = {
// test related code folder
include: ["source", "tests"],
exclude: [],
/** optional: assemblyscript compile flag, default is --exportStart _start -O0 */
flags: "",
/**
* optional: import functions
* @param {ImportsArgument} runtime
* @returns
*/
imports(runtime) {
return {
env: {
logInfo(ptr, len) {
if (runtime.exports) {
let arrbuf = runtime.exports.__getArrayBuffer(ptr);
let str = Buffer.from(arrbuf).toString("utf8");
console.log(str);
}
},
},
builtin: {
getU8FromLinkedMemory(a) {
return 1;
},
},
};
},
/** optional: template file path, default "coverage" */
// temp: "coverage",
/** optional: report file path, default "coverage" */
// output: "coverage",
/** optional: test result output format, default "table" */
// mode: ["html", "json", "table"],
};
The simplest way to test a value is with exact equality.
test('two plus two is four', () => {
expect(2 + 2).equal(4);
});
In this code, expect(2+2)
returns an "Value" object. You typically won't do much with these objects except call matchers on them. In this code, .equal(4)
is the matcher. When Jest runs, it tracks all the failing matchers so that it can print out nice error messages for you.
In the most condition, equal
is similar as ==
, you can use this matcher to compare i32 | i64 | u32 | u64 | f32 | f64 | string
just like ==
. What's more, it can also be used to compare some inner type, such as Array | Map | Set
.
However, Class and Interface cannot be compared directly now.
notEqual
is the opposite of equal
Most ways of comparing numbers have matcher equivalents, like equal
, greaterThan
, greaterThanOrEqual
, lessThan
, lessThanOrEqual
.
Specially, for float type, use closeTo
instead of equal
to avoid rounding error.
isNull
and notNull
matcher can be used to a nullable object.
Of cource, you can also use equal
and notEqual
to do same thing with explicit generic declartion expect<T | null>()
Because Assemblyscript's grammar is not as flexible as javascript, mock function have a lot of limitation and API design is not similar as jest (a javascript test framework).
However, There is a way to do some mock function.
Imagine that we are testing a function which includes a system-interface:
// source.ts
declare function sys_getTime(): i32;
export function getTime(): bool {
let errno = sys_getTime();
if (errno < 0) {
// error handle
return false;
}
// do something
return true;
}
To test error handle part, we need to inject some code to sys_getTime
and expect to return a errno.
// source.test.ts
test("getTime error handle", () => {
const fn = mock(sys_getTime, () => {
return -1;
});
expect(getTime()).equal(false); // success
expect(fn.calls).equal(1); // success
});
endTest();
mock API can temporary change the behavior of function, effective scope is each test. In this mock function, you can do every thing include expecting arguments, mock return values and so on.
Tips:
- Because Assemblyscript is a strongly typed language, you should keep the function signature aligned.
- AssemblyScript does not support closures. If a mock function needs to be called several times in one test, and you want it to return different values or match arguments to different values, using a global counter for this function is a good way to achieve this.
-
expect arguments
test("check argument", () => { const fn = mock(add, (a: i32, b: i32) => { expect(a).equal(1); return a + b; }); expect(fn.calls).greaterThanOrEqual(1); });
-
return difference value
const ret = [1,2,3,4,5,6,7,8]; // global variant const calls = 0; // global variant test("check argument", () => { const fn = mock(add, (a: i32, b: i32) => { return ret[calls++]; }); expect(fn.calls).greaterThanOrEqual(1); });
-
re-call origin
test("call origin", () => { mock(add, (a: i32, b: i32) => { unmock(add); const res = add(a,b); remock(add); return res; }); });
After testing, you can get a html / json / table format test coverage report include "Statements Coverage", "Branches Coverage", "Functions Coverage", "Lines Coverage"
assembly/
written in Assemblyscript, provides user-accessible testing APIs such as test, inspect, mock, etc.src/
written in Typescript, implements the test functionality.