layout | toc | title |
---|---|---|
docs |
creating-nodes-toc.html |
Testing |
This guideline explains how to write and execute test code for node.
Because nodes run in Node-RED, you cannot execute the processing of individual units of HTML or JavaScript to test processing. In addition, if you use manual testing, you need to stop and restart Node-RED after each test so that cleanup processing takes place. This makes testing quite time-consuming. Therefore, a tool called Node Test Helper (hereafter "helper") has been developed for Node-RED. This tool provides a framework for node testing. By using this framework, you can easily run nodes at the code level.
Node-RED uses the following libraries for testing, and we recommend their use when using the test code. You will need to have an understanding of the specifications of these libraries when creating test code. Read the documentation of each library and learn how to use it.
No. | Library | Purpose | URL |
---|---|---|---|
1 | Mocha | Test framework | https://mochajs.org |
2 | Should.js | Assertion | https://shouldjs.github.io |
3 | Sinon.js | Test double (general testing) | http://sinonjs.org |
4 | Nock | Test double (for creating mock HTTP server) | https://github.com/nock/nock |
5 | nyc | Coverage | https://www.npmjs.com/package/nyc |
Creating test code consists of the following steps:
- Preparing the environment
- Creating the test code
- Running the test code
Use npm
to install the modules required for testing in the directory in which you are developing your custom node.
npm install node-red-node-test-helper should mocha --save-dev
Install modules like sinon
and nock
as needed.
Create a spec file that contains the test directory and the test code.
You would typically create a test
directory in the same directory as package.json, and use the naming rule file-to-be-tested_spec.js
for the test files themselves.
[Example]
.
├── test-node.js
├── test-node.html
├── package.json
└── test
└── test-node_spec.js // As the spec file for test-node.js, the file name is test-node_spec.js
The processing of test code typically follows this sequence:
- Load libraries
- Initialize helper
- Start helper
- Exit helper
- Load test flow into helper
- Run test flow in helper
- Evaluate processing results
The following explains how to create test code based on the lowercase example in the helper. The test code is written using Mocha. Specific details about Mocha are not given here, but you can learn about writing test code in Mocha by visiting the preceding URL and other resources.
// 1. Load libraries
var should = require("should");
var helper = require("node-red-node-test-helper");
var lowerNode = require("../lower-case.js");
// 2. Initialize helper
helper.init(require.resolve('node-red'));
describe('myNode', function () {
// 3. Start helper
beforeEach(function (done) {
helper.startServer(done);
});
// 4. Exit helper
afterEach(function (done) {
helper.unload();
helper.stopServer(done);
});
// Test case
it('should make payload lower case', function (done) {
// Define test flow
var flow = [{ id: "f1", type:"tab"},
{ id: "n1", z:"f1", type: "lower-case", wires:[["n2"]] },
{ id: "n2", z:"f1", type: "helper" }];
// 5. Load test flow into helper
helper.load(lowerNode, flow, function () {
var lowerCase = helper.getNode("n1");
var helperNode = helper.getNode("n2");
// 7. Evaluate processing results
helperNode.on("input", function (msg) {
msg.should.have.property('payload', 'uppercase');
done();
});
// 6. Run test flow in helper
lowerCase.receive({ payload: "UpperCase" });
});
});
it('should do something', function (done) {
// Write test codes here ...
});
});
As a minimum, you will need to load the following libraries:
- should.js
- node test helper
- The node to be tested (lower-case.js in the example)
Add libraries like sinon.js as needed.
The helper does not in itself have the ability to run nodes and does so by referencing code in the Node-RED platform.
Give helper access to the code of the Node-RED platform by passing the Node-RED path to helper.init()
.
If you start helper and then run multiple test cases, the results of a test case might remain in helper and contaminate the results of other test cases.
For this reason, you must exit and restart helper after each test case.
You can make sure this takes place by calling the startup and shutdown of helper in beforeEach
and afterEach
functions.
Define the test flow to run as the test case, and load it into the helper. If your custom node has an output terminal and you want to evaluate its output results, we recommend that you create a flow using a helper node. A helper node is a mock node provided by the helper. You can check the output of a node you are testing by creating a flow in which a helper node is the succeeding node.
We recommend that you define a tab as part of a test flow (this corresponds to { id: "f1", type:"tab"},
in the example).
Although the code will run without a tab definition, if you do not specify one, the code is deployed internally to a tab named global
.
This means processing such as catch nodes and flow contexts that apply within a flow will not work correctly.
We recommend that you include a tab definition to better simulate a real-world environment.
Nodes that use credentials can be accommodated by passing arguments as shown in the following API request:
Load API request of Node Test Helper in Version 0.1.7
load(testNode, testFlows, testCredentials, cb)
No. | Argument | Required | Description |
---|---|---|---|
1 | testNode |
Yes | Node to be tested |
2 | testFlows |
Yes | Flow definition to be run by helper |
3 | testCredentials |
No | Credentials to pass to helper |
4 | cb |
Yes | Callback function executed after loading the flow |
Note: As in the preceding example, write the test code so that this step is executed after "7. Evaluate processing results".
Most nodes execute processing in response to a certain action, such as receiving a message from the preceding node.
This means that you need to write processing into the test case that triggers the execution of the node.
For nodes with input terminals (nodes whose processing is triggered by receiving a message from the preceding node), you can use receive
to pass a msg object to the node being tested as follows:
lowerCase.receive({ payload: "UpperCase" });
For nodes without an input terminal, you need to write the processing that the node expects. Because each node is executed in a different way, we have provided samples of core nodes for reference:
-
tail node
A tail node monitors a specific file, and when information is added to the file, sends the additions as a message to the succeeding node. Node execution is triggered by using anfs
module to add a character string to the file. -
websocket node
A websocket node can add a WebSocket server and send data received from a client to the succeeding node (or send data to the client). The node is executed by using aws
module to create a WebSocket client and sending data to it.
Use should.js
to write an expression that evaluates the processing results of the node.
If you want to evaluate the messages output by the node being tested, you can do so in an input
listener in the helper node, as shown in the example.
An input
listener is an event that is called when there is output from the preceding node.
It is passed as an argument of the message output of the preceding node.
For reference, sample code for node testing is provided at the following locations:
After creating the test code, you can run the test by executing the following command:
mocha \"test/**/*_spec.js\"
When testing a node that calls a custom system or other external service in the local environment, the processing of the node cannot be executed if the external service cannot be accessed from the local environment. You can test this kind of node in a way that does not depend on the environment by creating test code that uses a stub. Particularly in the case of unit testing, there is a good chance that the test code will be executed in different environments, such as that of another developer or a CI/CD environment. For this reason, we recommend that you create test code that does not depend on the environment. When the time comes to conduct integration testing or system testing, use the actual external system in the test instead of the stub.
By way of example, this section explains how to create a stub for a custom system (external service) that has an HTTP API.
-
Using nock to create a stub (recommended)
nock
is a module created to test HTTP requests. It implements a request stub by partially overriding the standardhttp
module.nock
allows you to create stubs with relatively simple coding and is easier to learn thanexpress
.- Examples of test code creation
- node-red-web-nodes test code
The test code for most nodes is created by using
nock
.
- node-red-web-nodes test code
The test code for most nodes is created by using
- Examples of test code creation
-
Using express to create a stub
express
is a framework for web application development. You can useexpress
to create a web server deployed locally, and send requests to that web server during testing. The web server works as a stub web server, which makes the test non-dependent on external factors. Consider creating a stub web server inexpress
when testing processing that includes special requests beyond the capabilities ofnock
.- Example of test code creation
- httprequest node
By creating a
testServer
in the test code that returns a specific response and issuing any requests totestServer
, you can perform testing without connecting to a web server outside the local environment.
- httprequest node
By creating a
- Example of test code creation
-
Using a test double
The use of a test double offers many advantages, including freedom from dependencies and the ease with which behavior can be verified. Make sure that you have a thorough understanding ofnock
orsinon
and are able to use it as the situation requires. -
Using settings.js during testing As of Version 0.1.7, Node Test Helper loads a substantially empty settings.js file that it creates internally. You cannot use a customized settings.js file during testing. If the node you are testing contains code that references settings in settings.js, you might be unable to test it using test code.
-
OS dependence
Node-RED runs on the Node.js platform and is generally OS-independent provided that Node.js is available. However, when executing functions that use OS resources such as file access or command execution, the behavior might differ depending on the operating system, especially when testing things like timeouts. Testing should ideally be conducted in the most commonly used OS environments, namely Linux, Windows, and macOS. To test the node on the various environment, online workflow services like Travis CI are useful.
- Testing package.json
When Node-RED loads a custom node, it references the package.json file associated with the node. If the code in package.json contains an error, Node-RED will be unable to load the node correctly. We recommend that you conduct a test in which you use thenpm
command to create an npm package for the custom node and load it in Node-RED.