I'm writing an npm module to interface with a piLite with node.js. I'd like to write it properly using TDD principles.
The code I need to test:
var SerialPort = require("serialport").SerialPort;
exports.PiLite = {
device: "/dev/ttyAMA0",
baudrate: 9600,
client: null,
init: function() {
this.client = new SerialPort(this.device, {
baudrate: this.baudrate
}, false);
},
connect: function(callback) {
this.init();
this.client.open(function() {
console.log('Connected to Pi Lite');
callback();
});
},
write: function (data) {
...
The standard usage would be:
var pilite = require('pilite').PiLite;
pilite.connect(function() {
pilite.write('some data');
// calls to functions to send messages to pilite
}
I understand how to test assertions but I don't see how I can test the connectivity to the serial port.
Should I test for it or just test the functions I'm using to write to the serial port?
Edit: I'm pretty new to Nodeunit so any pointers in the right direction would be great.
Related
First off, I'm new to both node.js and the concept of async functions and would greatly appreciate any help I can get here.
I've been trying to write a script using the node.js serialport module to scan the ports on my Windows machine and do simple stuff once an Arduino Micro is connected to a port. The below works fine if the Arduino is already connected, but I can't work out how to extend it so that it will wait, indefinitely, until the Micro is plugged in. It just terminates if nothing is found.
const serialPort = require('serialport');
var portName;
serialPort.list().then(function(ports){
ports.forEach(function(portInfo){
if (portInfo.vendorId == 2341 && portInfo.productId == 8037) {
portName = portInfo.path;
var myPort = new serialPort(portName);
myPort.on('open' , function() {
showPortOpen();
myPort.write('RL'); // command to initiate functions in Arduino code
});
myPort.on('data' , readSerialData); // echo data from Arduino
myPort.on('close', showPortClose);
}
})
});
function readSerialData(data) {
console.log(data);
}
function showPortOpen() {
console.log(portName,'opened');
}
function showPortClose() {
console.log(portName,'closed');
}
Problem solved in double-quick time. Thank you :-)
Not sure if it's the cleanest approach but by re-calling the setInterval function when the port closes, I have a script that waits and finds the Arduino once it's plugged into the USB and, if subsequently unplugged, will find it again once it's plugged in again. Just what I want!
const serialPort = require('serialport');
var portName;
loop(); // start searching for Arduino on a port
function loop() {
loopId = setInterval(function() {
serialPort.list().then(function(ports){
ports.forEach(function(portInfo){
if (portInfo.vendorId == 2341 && portInfo.productId == 8037) {
portName = portInfo.path;
var myPort = new serialPort(portName);
myPort.on('open' , function() {
showPortOpen();
myPort.write('RL'); // command to initiate Arduino functions
});
myPort.on('data' , readSerialData); // echo data from Arduino
myPort.on('close', showPortClose);
}
})
})
}, 1000)
};
function readSerialData(data) {
console.log(data);
}
function showPortOpen() {
console.log(portName,'opened');
clearInterval(loopId); // stop looping once Arduino found on port
}
function showPortClose() {
console.log(portName,'closed');
loop(); // start over when Arduino port is closed
}
I have an array like this:
var exports = module.exports = {};
var net = require('net');
exports.config = [
{
ip: "127.0.0.1",
id: 1,
socket: new net.Socket(),
data: "",
},
{
ip: "192.168.5.242",
id: 2,
socket: new net.Socket(),
data: "",
}
];
I'm trying to connect each of this items with a TCP socket with this code:
for(var key in tornelli.config) {
tornelli.config[key].socket.connect(4000, tornelli.config[key].ip, function() {
console.log('CONNECTED TO: '+tornelli.config[key].ip);
});
tornelli.config[key].socket.on('data', function(data) {
...........
});
tornelli.config[key].socket.on('error', function(error){
console.log(error);
});
}
But somethings get wrong because in the console output I get
CONNECTED TO: 192.168.5.242
But actually I'm connected with '127.0.0.1'. It seems that I don't separate each tcp stream. How can I have two separate tcp stream, one every item in this array?
It's somethings about asynchronous execution?
Thanks
In Javascript, for loops are not "blocks". You can put var in there if you want but it will still be "hoisted" to the top of your function. Everything is fine in your initial function calls - key will be 0, and then 1, and the loop will run twice. But when the CALLBACKS are executed, key will always be 1 - its last value. The callbacks are asynchronous, so they will be fired well after the for loop has done its thing.
Javascript has a forEach operator that makes tasks like this simpler. Try the following (or simply break out your socket-handling code to a function that takes the config block as a parameter):
tornelli.config.forEach(function(config) {
config.socket.connect(4000, config.ip, function() {
console.log('CONNECTED TO: '+config.ip);
});
config.socket.on('data', function(data) {
...........
});
config.socket.on('error', function(error){
console.log(error);
});
}
For what it's worth, it's also really unusual to pre-create things like sockets inside config files. A more standard pattern would be to do the new net.Socket() call inside the above code, not in the config file.
I'm writing a nodejs module to abstract the detail of driving motors connected to Raspberry Pi's gpio headers.
The node module I'm using for communicating with the pi's gpio is pi-gpio
I'm testing my module with jasmine, and because I know the pi-gpio module that I require has specific hardware requirements and that it won't run on my development PC (because it's not a pi), I am using proxyquire to stub the dependency.
Consider the following (simplified) module:
nodepibot.js
'use strict';
var gpio = require("pi-gpio");
const LOW = 0,
HIGH = 1;
const MOTOR_LEFT_ENABLE = 22,
MOTOR_LEFT_A = 16,
MOTOR_LEFT_B = 18;
var leftMotorStop = function leftMotorStop() {
gpio.write(MOTOR_LEFT_ENABLE, LOW, function (err) {
if (err) {
throw err;
}
});
};
module.exports = {
"leftMotorStop": leftMotorStop
};
And my jasmine spec:
nodepibotSpec.js
describe("Node Pibot tests", function() {
var nodepibot,
stubGpio = {
write: function() {}
};
beforeEach(function() {
var proxyquire = require('proxyquire').noCallThru();
nodepibot = proxyquire("../main/nodepibot", {'pi-gpio': stubGpio});
});
it("Should stop left motor", function() {
// Given
spyOn(stubGpio, "write");
// When
nodepibot.leftMotorStop();
// Then
expect(stubGpio.write.callCount).toBe(1);
expect(stubGpio.write).toHaveBeenCalledWith(22, 0, jasmine.any(Function));
});
});
The above tests my 'happy path' - IE. I am asserting that when leftMotorStop is called on my module, then the write method is called on the pi-gpio library with appropriate parameters.
What I would like to be able to do is test the un-happy path - IE. to test the anonymous callback function when err has a value in which case I assert an exception is thrown back.
Does anyone know how I would go about this?
Just rewrite your stubbed write function, to call the error function:
var nodepibot,
stubGpio = {
write: function(var1, var2, callback) {
callback("stubbed error");
}
};
I am wondering is it possible to run methods provided in node-ssh2 in a blocking way.
I am testing my code with node-vows.
snippet of conn-test.js
suite = vows.describe("conn-test");
suite.addBatch({
topic: function () {
return new Connection("1.2.3.4", "root", "oopsoops");
}
"run ls": function (conn) {
conn.send("ls");
}
});
snippet of conn.js
var ssh2 = require("ssh2");
function Connection(ip, user, pw) {
//following attributes will be used on this.send()
this.sock = new ssh2();
this.ip = ip;
this.user = user;
this.pw = pw;
}
Connection.prototype.send = function (msg) {
var c = this.sock;
//Copy the example 1 on https://github.com/mscdex/ssh2
}
Node-Vows runs my code without errors. However, the problem is vows terminated faster than callback from ssh2. In other word, I cannot get response from ssh2.
It seems that node-async is one of the possible solution. However, I have no idea how to force the event driven calls becomes a blocking call by the help of async.
Anyone can help?
--Updated 10/04/2014
Fix the typo on title....
I've never used vows before, but according to their reference documentation, you should use this.callback instead of returning a value. Your vows code might instead look something like:
var ssh2 = require('ssh2'),
assert = require('assert');
suite = vows.describe('conn-test');
suite.addBatch({
topic: function() {
var conn = new ssh2.Connection(),
self = this;
conn.connect({
host: '1.2.3.4',
port: 22,
username: 'root',
password: 'oopsoops'
});
conn.on('ready', function() {
self.callback(conn);
});
},
'run ls': function(conn) {
var self = this;
conn.exec('ls', function(err, stream) {
assert(!err);
stream.on('exit', function(code, signal, coredump) {
assert(code === 0);
assert(!signal);
assert(!coredump);
self.callback();
});
});
}
});
// connections.js
...
module.exports = {
conn: {
mongodb: {
connect: function() {throw ...},
disconnect: function() {throw ...},
getState: function() {throw...}
},
mysql: {
connect: function() {throw ...},
disconnect: function() {throw ...},
getState: function() {throw ...}
}
},
drivers: {
mongoose: mongoose,
mysql: mysql
},
states: connectionStates,
setup: function(config, cb) {
// provides concrete implementations of connect(), discconnect(),
// getState(), sets up listeners to relay connection events
this.conn.mongodb = setupMongo(config.mongodb);
this.conn.mysql = setupSql(config.mysql);
...
cb();
}
};
Now if I include this as:
// main.js
var connections = require(__dirname + '/connections'),
conn = connections.conn,
db = conn.mongodb;
// connectionections.setup() not been called yet
exports.foo = function() {
// connections.setup() already been called before this point
db.connect(...); // fails - error thrown - using the abstract function
conn.mongodb.connect(...); // works
}
Why does the first one fail? The db var should contain a reference to connections.conn.mongodb? At very least, I'd expect both to either work, or not work. What is the difference that allows the first to fail and second to succeed? Thank you
Its failing in the first case because setup() was called in a different scope and db/conn.mongodb diverged (with a copy on write) when setup was called. If you compare db and conn.mongodb in the exports.foo function, you should see that conn.mongodb has been initialized with the setupMongo and db still has the uninitialized versions. Not sure what the code looks like that is calling connections.setup, but from the looks of this, db !=== conn.mongodb.