Skip to main content

Tests

Package Execution

A package can be executed by an other package with the command execute_package. This is different to libraries. A library is executed as part of the using package. A package executed with "execute_package" is executed for itself and execution data can be provided or is taken from the calling package.

With this technique the subsequent call of multiple packages can be implemented to keep code small and maintainable.

But it is also possible to create test packages that call the implementation packages with test data. This is similar to do a manual Debug execution - but it is done by code, without any real event.

Example :

let data = {
source: { event: 'request-update' },
event: 'request_update',
mail: null,
initial_id: 15,
module: 'requests',
varname: 'request',
isApiUser: false,
mockData: {}
}

let result = execute_package('do_on_req_upd', data)
log('result', result)

The advantage of this approach is that the package can be called multiple times considering different test data.

Mock Functions

To Mock a function means to replace their execution by a standard return value or an other implementation. This is useful for testing reasons to avoid e.g. update or create http or rest calls to foreign accounts.

Mocking is done with setMock:

Example :

function x(p) {
return p
}

(function () {
let res_1 = x(5)

setMock('x', 'fixed mocked result')

let res_2 = x(5)

setMock('x', (p) => {
return 'mocked function'
})

let res_3 = x(5)

setMock('x')

let res_4 = x(5)

log('res_1', res_1) // 5
log('res_2', res_2) // 'fixed mocked result'
log('res_3', res_3) // 'mocked function'
log('res_4', res_4) // 5

}())

In the example the function x is mocked - the mock function must have the same number of parameters. Any function can be mocked - especially create, update, rest_get, rest_put, rest... , http_get, ...

A mocked function is valid only for the current package execution If there is a special need to reset to the original functionality just call setMock without any further parameter as shown in the example.

Package Execution and Libraries

Package Execution and Libraries fit seamless together with mocking of functions.

The package_execute command allows to set a test library that is added to the executed package. Inside this "added" test library all functions in the executed package that can harm data can be mocked. This test library is used as part of the executed package - it must not be included as standard library in the package settings editor.

Example :

Lets write a simple package that is activated by a request_update webhook, and that adds a simple test note to the updated request:

package "do_on_request_update"

(function () {
update('requests', request.id, { note: 'Test note' })
}())

This package can be executed manually, and on every execution a real note is added.

To make this testable create a test library with a mock for the update function:

library "do_on_request_update - MOCK"

setMock('update', (module, id, data) => {
log('update is called for id:', id)
log('with data:', data)
return data
})

When this library is used it avoids any call to "update". It just writes a text to the log.

With execute_package this can be used.

Write a test package that does the execute of the standard package using the test mock library:

package "do_on_req_upd - TEST"

let execute_data = {
source: { event: 'request-update' },
event: 'request_update',
mail: null,
initial_id: 15,
module: 'requests',
varname: 'request',
isApiUser: false,
}

execute_package('do_on_request_update', execute_data, 'do_on_request_update - MOCK')

With the call of execute_package the library "do_on_request_update - MOCK" is used as part of the called package.

With this technique any package can be tested multiple times by code without changing any real data or the need of reset data in foreign accounts.

Whenever implementation code changes, this test package with the mock library can be used to check if all works fine.

Test Functions

Using the test function Test Driven development is possible:

test('Test description',[params], (p)=>{
// testcode
assert_equal(actual, expected, failure_text)
})

The optional params array can contain parameter objects. The test function is repeated called with each of the params values.

Example:

// function to test
function getAcountingPeriodByDate(dt) {
let year = dt.getFullYear()
let month = dt.getMonth() + 1
return {
AccountingPeriodYear: year,
AccountingPeriodMonth: month
}
}

// Tests
// without Params
test('test without Params', () => {
let dt = new Date(2017, 12, 13)
let dtt = getAcountingPeriodByDate(dt)

assert_equal(dtt.AccountingPeriodYear, 2017, 'Wrong year value')
assert_equal(dtt.AccountingPeriodMonth, 12, 'Wrong month value')
})

// with Params
let test_param = { y: 2017, m: 12, d: 13 }
test('test with Params', test_param, (param) => {
let dt = new Date(param.y, param.m, param.d)
let dtt = getAcountingPeriodByDate(dt)

assert_equal(dtt.AccountingPeriodYear, param.y, 'Wrong year value')
assert_equal(dtt.AccountingPeriodMonth, param.m, 'Wrong month value')
})

// with Params list
let test_params = [
{ y: 2017, m: 12, d: 13 },
{ y: 2017, m: 1, d: 1 },
{ y: 2017, m: 12, d: 31 }
]

test('test multi Params', test_params, (param) => {
let dt = new Date(param.y, param.m, param.d)
let dtt = getAcountingPeriodByDate(dt)

assert_equal(dtt.AccountingPeriodYear, param.y, 'Wrong year value')
assert_equal(dtt.AccountingPeriodMonth, param.m, 'Wrong month value')
})

Grouping Tests

Test can be grouped in a hierachy. If a test fails the group fails immediately. Execution stops at the first failed test.

The name of the group is written in front of the test name - so a short group name is a good choice. The result of the group is logged at the end of all containing tests.

By default there is no success log for each assert, only on failure.

Test Parameters are possible on each level of the grouping.

Example:

test('Set 1', () => {
test('Group 1', () => {
test('Test 1', () => {
assert_equal(1, 1, 'one is not one');
})
})

test('Group 2', () => {
test('Test 1', () => {
assert_equal(2, 2, 'two is not two');
})

test('Test 2', () => {
assert_equal(3, 3, 'three is not three');
})
})
})