Units Testing on a Node

Hi,

How does one set a configuration node values in a unit test?

Thank you

What node are you talking about??
What is your definition of a ‘unit test’?

I have a node:

// connect.js

module.exports = function (RED) {
  // Define connect node.
  function ConnectNode (config) {
    // Create node.
    RED.nodes.createNode(this, config)

    // Get values from "settings" configuration node.
    this.settings = RED.nodes.getNode(config.settings)
    ...
  }
  ...
}

And it is loaded onto a unit test:

// connect_spec.js

const helper = require('node-red-node-test-helper')
const ConnectNode = require('../nodes/config/connect')

helper.init(require.resolve('node-red'))

describe('Connect Node', function () {
  beforeEach(function (done) {
    helper.startServer(done)
  })

  afterEach(function (done) {
    helper.unload()
    helper.stopServer(done)
  })

  // This test passes.
  it('should be loaded', function (done) {
    const flow = [{ id: 'n1', type: 'connect', name: 'Connect to EC25', settings: { verbose: 4, simulation: true, serialPort: '', audioDevice: '' } }]
    helper.load(ConnectNode, flow, function () {
      const n1 = helper.getNode('n1')
      n1.should.have.property('name', 'Connect to EC25')
      n1.should.have.property('settings')
      done()
    })
  })

  // This test hangs
  it('should connect', function (done) {
    const flow = [{ id: 'n1', type: 'connect', name: 'Connect to EC25', settings: { verbose: 4, simulation: true, serialPort: '', audioDevice: '' }, wires: [['n2']] },
      { id: 'n2', type: 'helper' }
    ]
    helper.load(ConnectNode, flow, function () {
      const n1 = helper.getNode('n1')
      const n2 = helper.getNode('n2')
      n2.on('input', function (msg) {
        msg.should.have.property('payload', 'hello world!')
        done()
      })
      n1.receive({ payload: "hello world!" })
    })
  })
})

The problem I am facing is, that execution hangs when a configuration node's value is read:

// connect.js

    // Get values from "settings" configuration node.
    this.settings = RED.nodes.getNode(config.settings)
    // Test halts when this.settings is read
    console.log(this.settings.serialPort)

It seems like the unit test has to "load" the config node, I do not know how this works in the backend of things. It would be great if there is a way to initialize a configuration node in a unit test. Right now it seems to be reading some uninitialized value which hangs the test.


Thanks

Looking at your node's js file, you do this:

this.settings = RED.nodes.getNode(config.settings)

That implies you expect config.settings to be the id of a configuration node.

In you test flow, you are providing a configuration of:

{ id: 'n1', type: 'connect', name: 'Connect to EC25', settings: { verbose: 4, simulation: true, serialPort: '', audioDevice: '' } }

Here, settings is not the id of a config node - its an object of key/value pairs.

So the question is more about how you want this to work.

If you want to use a Config node, where have you defined it in the .js file? What type does it have?

I want to use a config node. The type is "settings"

// settings.html

<script type="text/javascript">
    RED.nodes.registerType('settings', {
        category: 'config',
        color: '#8d6b94',
        defaults: {
            verbose: { value: 4, required: true, validate: RED.validators.number() },
            simulation: { value: true, required: true },
            serialPort: { value: "/dev/ttyUSB3", required: true },
            audioDevice: { value: "sysdefault:CARD=EC25", required: true }
        },
        label: function () {
            return this.verbose + ":" + this.simulation + ":" + this.serialPort + ":" + this.audioDevice;
        }
    });
</script>

<script type="text/html" data-template-name="settings">
    <div class="form-row">
        <label for="node-config-input-verbose"><i class="fa fa-bookmark"></i> Verbose Logging</label>
        <select name="node-config-input-verbose" id="node-config-input-verbose">
            <option value="0">NONE</option>
            <option value="1">ERROR</option>
            <option value="2" selected="selected">ERROR, WARNING</option>
            <option value="3">ERROR, WARNING, INFO</option>
            <option value="4">ERROR, WARNING, INFO, DEBUG</option>
        </select>
    </div>
    <div class="form-row">
        <label for="node-config-input-simulation"><i class="fa fa-bookmark"></i> Simulation</label>
        <input type="checkbox" id="node-config-input-simulation">
    </div>
    <div class="form-row">
        <label for="node-config-input-serialPort"><i class="fa fa-bookmark"></i> Serial Port Name</label>
        <input type="text" id="node-config-input-serialPort">
    </div>
    <div class="form-row">
        <label for="node-config-input-audioDevice"><i class="fa fa-bookmark"></i> Audio Device Name</label>
        <input type="text" id="node-config-input-audioDevice">
    </div>
</script>
// settings.js

module.exports = function (RED) {
  function SettingsNode (n) {
    RED.nodes.createNode(this, n)
    this.verbose = n.verbose
    this.simulation = n.simulation
    this.serialPort = n.serialPort
    this.audioDevice = n.audioDevice
  }
  RED.nodes.registerType('settings', SettingsNode)
}

I tried adding it to the unit test's flow, but no luck with that.

const flow = [{ id: 'n1', type: 'connect', name: 'Connect to EC25', settings: "n3", wires: [['n2']] },
      { id: 'n2', type: 'helper' },
      {
        id: 'n3',
        type: 'settings',
        verbose: '4',
        simulation: true,
        serialPort: '/dev/ttyUSB3',
        audioDevice: 'sysdefault:CARD=EG25G'
      }
    ]

I do not know if I have to "import" the SettingsNode somewhere in the unit test.

My immediate feedback is don't call it settings. Types must be unique - calling your node something as general as that is very likely to cause conflicts. Likewise connect is too generic - give them a name that is more related to whatever it is connecting to.

So, with that extra information, you need a test flow that includes both the connect node and its settings node.

[
   { id: 'n1', type: 'connect', name: 'Connect to EC25', settings: "my-settings-node", wires: [['n2']] },
   { id: "my-settings-node",  verbose: 4, simulation: true, serialPort: '', audioDevice: '' },
   { id: 'n2', type: 'helper' }
 ]

You also need to tell the test helper about the config node:

const ConnectNode = require('../nodes/config/connect')
const SettingsNode = require('../nodes/config/settings')
...
helper.load([ConnectNode, SettingsNode], flow, function () {

But as I said at the start - pick more specific node type names.

I see, this makes a lot of sense. There was no documentation on loading multiple nodes for the helper module. I will give this a try. Thank you!

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