I'm a very happy Node Red user. I use it to do all of my automations with Home Assistant, rather than try to "program" in YAML. I'm a software developer as a day job, so the ability to drop down into Javascript at any point (in function nodes) is the killer feature for me. It enables me to keep the dependencies low, for example.
However, one I thing I miss is the ability to do Test-Driven Development. I can't count how many times I've done a refactoring only to discover a week (and several commits) later that the outside shed light turns on when I enter the upstairs loo because of a copy & paste error... Being able to refactor in confidence is one of the big perks of TDD.
How does that translate to a low-code environment like Node Red? Do you have test nodes? How does that work?
I can't say I do TDD but in many places inside my node-red flows I insert 'Debug' (blocks of code that inject known test data to force my application to respond correctly / incorrectly).
Something like this:
I can see how you could easily create a separate Flow (page) that only contains TDD elements that are triggered in a cascade. Delay timers or completion events can let one test finish before the next starts to prevent collisions.
Link In / Link Call / Link Out can be used as the connection point between the tests and the live code.
I have a somewhat similar need, for invoking test & troubleshooting scenarios in runtime in a production environment, where I cannot modify & deploy flows as this will disrupt on-going activities.
The solution I found was to write a pass-through custom node, which executes an "on msg" function that dynamically loads itself from a selected file in runtime. The node is controlled by commands such as enable, disable, reload from etc.
This way I modify behavior in runtime, without deploying/restarting the flows. Note that this can be a security vulnerability, as it provides a back door to the system.
I'm not familiar with the logistics of sharing a node, I can send the whole package privately to whoever finds it useful.
In any case, the logic of my "Tester" node is very simple: you just need define one or more JS files which export a function with the hard-coded name dynFunc, which you can then load, change or replace in runtime. For example:
// File C:/Temp/Hello1.js
exports.dynFunc = dynFunc;
function dynFunc(node,config,msg,send,done) {
msg.payload = "Hello from Function 1";
send(msg);
done();
}
The "on msg" handler checks for the property msg.importFile. if it exists, the message is treated as a control message, which loads, reloads or clears the import.
if (!importFile) // Clear the current import
{
if (node.importFile)
delete require.cache[require.resolve(node.importFile)];
node.importFile = null;
node.dynImport = null;
msg.payload = "Import cleared";
}
else
{
if (importFile !== node.importFile || msg.forceReload) // Import a new file or reload the existing file
{
try {
if (node.importFile)
delete require.cache[require.resolve(node.importFile)]; // Clear the import cache
node.dynImport = require(importFile);
node.importFile = importFile;
msg.payload = `Succesfully imported file ${importFile}`;
}
catch(err) {
msg.payload = `Failed to import file ${importFile}`;
msg.error = err.message;
}
}
else
msg.payload = `File ${importFile} already loaded`;
node.send(msg);
done();
return;
}
Otherwise, the message is treated as a data message, and sent to the dynamic function:
if (node.dynImport)
node.dynImport.dynFunc(node,config,msg,send,done);
else // In case there is no import, forward original msg as-is
{
node.send(msg);
done();
}
The code should be uploaded to GitHub ideally with git used locally to manage changes and to sync to the upstream GitHub.
But the main point is that npm has a command called publish which will push the whole package to npmjs.org. Of course, you need to define a suitable package.json file before doing that. It takes a few minutes to set up once you've done some background reading.
Even if you decided not to publish to npm, having the code on GitHub will allow anyone to manually install it to Node-RED.