Using Subflows within Node-RED unit tests (node-red-node-test-helper) does NOT work

Hello together,

I have created several unit tests using node-red-node-test-helper to have end-to-end tests of my flow containg 4 nodes:
A input
B function
C function
D output

Test worked without any problems for the whole chain sending input values and validate message on output node.

I created a subflow "SUB" containing the function nodes B and C.
So the main flow is
A - SUB - D

My expection was that unit test should still work.
But they do not.

Is there a trick how flows containing subflows can be tested?

BTW flow is still working in Node-RED, just test is no longer working

Thanks in advance

Junit is for Java. How are you doing the flow tests?

Can you share more detail or a repo?

ups yeah sure...unit test :slight_smile:

const requiredNodes = [
		require("../node_modules/@node-red/nodes/core/function/15-change.js"),
		require("../node_modules/@node-red/nodes/core/common/20-inject.js"),
		require("../node_modules/@node-red/nodes/core/common/21-debug.js"),
		require("../node_modules/@node-red/nodes/core/function/10-function.js"),
		require("../node_modules/@node-red/nodes/core/function/10-switch.js"),
		require("../node_modules/@node-red/nodes/core/function/89-delay.js"),
		require("../node_modules/@node-red/nodes/core/network/10-mqtt.js")
	]

const flow: any[] = require("../flows.json")

it('Verify test flow', function (done) {
	helper.load(requiredNodes, flow, function () {

		// given is flow input
		var inputNode = helper.getNode('A')
		expect('Input Node Name').to.equal(inputNode.name)
		
		// and flow output
		var outputNode = helper.getNode('D')
		expect('Output Node Name').to.equal(outputNode.name)

		// when payload is sent
		inputNode.wires[0].forEach((wire: string) => {
			const node = helper.getNode(wire)
			node.receive(
				{
					payload: false
				}
			)
		})

		// then output message must be created
		outputNode.on("input", (msg: any) => {
			try {
				expect(false).to.equal(msg.payload)
				done()
			} catch (e) {
				done(e)
			}
		})
	})
})

To stay on the origin example
I have an input node A and an output node D
expected is that when input node A sends payload false output node D should receive payload false.

When in my flow configuration B and C are function nodes everything works as expected.

But as soon as I move the nodes B and C to a subflow, no value is received on D.

Do I have to load the subflow or do i need to do anything else?

Does somebody have an example test with an subflow in the middle of a flow?

Are you able to provide the flow.json content?

I wrote test-helper for unit testing individual nodes. I'm not sure I've ever seen it used to test subflows. So I simply can't say if I'd expect that to work or not.

Yes I created a test project:

TestSubflow

Node B and C are equal in TestFlow and TestSubflow

Flow file:

[
    {
        "id": "0cc4be7a8aa4d1d2",
        "type": "tab",
        "label": "Test Flow",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "842d78cb52b06cf4",
        "type": "subflow",
        "name": "TestSubflow",
        "info": "",
        "category": "",
        "in": [
            {
                "x": 160,
                "y": 100,
                "wires": [
                    {
                        "id": "c6d4d64122fe4930"
                    }
                ]
            }
        ],
        "out": [
            {
                "x": 1020,
                "y": 100,
                "wires": [
                    {
                        "id": "48ba4b4257418165",
                        "port": 0
                    }
                ]
            }
        ],
        "env": [],
        "meta": {},
        "color": "#DDAA99"
    },
    {
        "id": "c6d4d64122fe4930",
        "type": "function",
        "z": "842d78cb52b06cf4",
        "name": "B",
        "func": "msg.payload = !msg.payload\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 430,
        "y": 100,
        "wires": [
            [
                "48ba4b4257418165"
            ]
        ]
    },
    {
        "id": "48ba4b4257418165",
        "type": "function",
        "z": "842d78cb52b06cf4",
        "name": "C",
        "func": "msg.payload = !msg.payload\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 770,
        "y": 100,
        "wires": [
            []
        ]
    },
    {
        "id": "c7cb8f9cd5294ade",
        "type": "inject",
        "z": "0cc4be7a8aa4d1d2",
        "name": "Input",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "false",
        "payloadType": "bool",
        "x": 190,
        "y": 120,
        "wires": [
            [
                "c3ba6884ecee7685"
            ]
        ]
    },
    {
        "id": "a0021e7915c9c92e",
        "type": "function",
        "z": "0cc4be7a8aa4d1d2",
        "name": "B",
        "func": "msg.payload = !msg.payload\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 450,
        "y": 120,
        "wires": [
            [
                "465abef1163f8028"
            ]
        ]
    },
    {
        "id": "465abef1163f8028",
        "type": "function",
        "z": "0cc4be7a8aa4d1d2",
        "name": "C",
        "func": "msg.payload = !msg.payload\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 790,
        "y": 120,
        "wires": [
            [
                "a4ce7070377a10c1"
            ]
        ]
    },
    {
        "id": "a4ce7070377a10c1",
        "type": "debug",
        "z": "0cc4be7a8aa4d1d2",
        "name": "Output",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1190,
        "y": 120,
        "wires": []
    },
    {
        "id": "c3ba6884ecee7685",
        "type": "subflow:842d78cb52b06cf4",
        "z": "0cc4be7a8aa4d1d2",
        "name": "",
        "x": 630,
        "y": 220,
        "wires": [
            [
                "a4ce7070377a10c1"
            ]
        ]
    }
]

Unit test:

import "mocha"
import { expect } from "chai"
import * as sinon from "sinon"
var should = require("should")


describe("my flowtest", () => {

	const helper = require("node-red-node-test-helper")
	
	const requiredNodes = [
		require("../node_modules/@node-red/nodes/core/function/15-change.js"),
		require("../node_modules/@node-red/nodes/core/common/20-inject.js"),
		require("../node_modules/@node-red/nodes/core/common/21-debug.js"),
		require("../node_modules/@node-red/nodes/core/function/10-function.js"),
		require("../node_modules/@node-red/nodes/core/function/10-switch.js"),
		require("../node_modules/@node-red/nodes/core/function/89-delay.js"),
		require("../node_modules/@node-red/nodes/core/network/10-mqtt.js"),
		//require("../node_modules/@node-red/runtime/lib/flows/Subflow.js")
	]

	const loadFlowFile = () => {
		const TYPE_IGNORE_LIST: string[] = ['change', 'function', 'switch', 'delay','subflow']
		const flow: any[] = require("../flow.json")

		// convert unknown types to helper
		flow.forEach((node) => {
			if (!TYPE_IGNORE_LIST.some(e => e === node.type)) {
				node.type = "helper"
			}
		})
		return flow
	}

	const flow: any[] = require("../flow.json")

	before(() => { helper.init(require.resolve('node-red'))})

	beforeEach((done) => { helper.startServer(done)	})

	afterEach(function (done) {
		try {
			helper.unload()
		} catch (err) {
		}
		helper.stopServer(done)
	})

	it('subflow test', function (done) {
		this.timeout(1000)
		
		helper.load(requiredNodes, loadFlowFile(), function () {
			try {
				// given is flow input
				var inputNode = helper.getNode('c7cb8f9cd5294ade')
				expect('Input').to.equal(inputNode.name)

				// and output
				var outputNode = helper.getNode('a4ce7070377a10c1')
				expect('Output').to.equal(outputNode.name)

				// when payload is sent
				inputNode.wires[0].forEach((wire: string) => {
					const node = helper.getNode(wire)
					node.receive({ payload: false })
				})

				// then message must be receivedwith payload false
				outputNode.on("input", (msg: any) => {
					try {
						expect(false).to.equal(msg.payload)
						done()
					} catch (e) {
						done(e)
					}
				})
			} catch (e) {
				done(e)
			}
		})
	})

})

If you change the wire from subflow (c3ba6884ecee7685) to node B (a0021e7915c9c92e) of the inject node, then unit testis working.
But with using subflow it is not working.

I think the problem is that subflow is not correctly registered as requiredNodes.

Hopefully this information helpsfor some support

ok thanks for your answer.

How do you usually create integration tests to verify whole flows ?

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.