I have a couple of Philip Hue lights in the hallway. These show the build status on their lights with a small Node.JS application. Its working like a charm.
Currently I am in the process of testing and refactoring this application, but in my app.js
I have some lines of code where I am having a hard time to figure out how to automatically test them.
if(config.enabledHueUpdateJobs) {
var Hue = require('./hue.js');
var Lights = require('./lights.js');
var hallwayLights = new Lights(new Hue());
hallwayLights.scheduleOn('45 7 * * 1-5');
hallwayLights.scheduleOff('30 17 * * 1-5');
hallwayLights.scheduleUpdate('*/30 * 7-18 * * 1-5', buildServer.getFailedBuildCount);
}
I have 100% coverage for the Hue.js
and the Lights.js
'classes', but the actual implementation code is not covered. I have had an issue where I changed the 'classes' public methods, but not the implementation code, but this was not caught by my tests.
I have been thinking about moving this code to a 'class' with a .start()
function, but then still I would have one or two untested lines.
Please suggest a way how I can restructure my code (or some best practices/patterns that I should research/use) to make this better testable.
Update:
- I want to let our build-server deploy a working version after all my tests have run. So I don't have to check the system manually.
- I would like to have a TDD workflow, I would like to stay in my IDE and be confident to push my final changes.
- Preferably I do not want to start 'node app.js' and test it with some blackbox testing system
-
1Um, how do you actually get feedback from a Phillip Hue light to prove that your tests work? Are you using some sort of calibrated color sensor?Robert Harvey– Robert Harvey2016年11月17日 17:28:01 +00:00Commented Nov 17, 2016 at 17:28
-
In addition, what value do such tests bring to the table that a visual inspection of the output of the bulb doesn't already provide?Robert Harvey– Robert Harvey2016年11月17日 17:28:58 +00:00Commented Nov 17, 2016 at 17:28
-
I don't really think I need feedback from the lights, I am already mocking the API. Also I do not want to test the lamps have the right color, I trust the Hue API to work and be wel tested itself.Niels van Reijmersdal– Niels van Reijmersdal2016年11月17日 19:13:50 +00:00Commented Nov 17, 2016 at 19:13
-
I just want to make sure when I am adding a new feature TDD style or when refactoring that I don't forget to update the code in app.js. For example I could rename the Lights class to HallwayLamps, because I think it reads better, all my tests would succeed on the build-server and the build-server will auto deploy to the production system. But if I forgot to update the app.js the system is broken. I just want to cover the actual implementation with tests so I can safely update the system without manual testing it.Niels van Reijmersdal– Niels van Reijmersdal2016年11月17日 19:13:56 +00:00Commented Nov 17, 2016 at 19:13
-
1RobertHarvey, thanks for calling it an integration test. Laiv, thanks for your ideas. I think parameterizing the interface helped to push me into my current solution.Niels van Reijmersdal– Niels van Reijmersdal2016年11月18日 10:33:13 +00:00Commented Nov 18, 2016 at 10:33
1 Answer 1
I found a working solution to write an integration-test for my code. I will try to explain my implementation.
I have added a way to start and stop my code in app.js. (I was already using a similar strategy for my Selenium tests for another project) This way I can start and stop the app.js code from the tests, without starting 'node app.js'
from the command-line.
app.js code:
exports.start = function () {
buildServer.updateCaches();
var hallwayLights = new Lights(new Hue(config.hueServerUrl, config.hueUserName));
jobs.push(hallwayLights.scheduleOn(config.lightsOnSchedule));
jobs.push(hallwayLights.scheduleOff(config.lightsOffSchedule));
jobs.push(hallwayLights.scheduleUpdate(config.lightsUpdateSchedule, buildServer.getFailedBuildCount));
}
exports.close = function() {
jobs.forEach(function (job) {
job.cancel();
});
}
if (!isStartedFromTests()) { exports.start(); }
function isStartedFromTests() { return module.id != require.main.id; }
In the tests (using ava) I nock all the expected external API calls and verify they have been called.
test.integration.js code:
test.cb('Radiator: Should execute light schedules When the app is started', t => {
// Arrange
t.plan(3);
var bsNocks = new buildServerNocks(config.buildserverServerName, config.buildserverPort);
bsNocks.buildSuccess('Project1_Build1');
bsNocks.buildSuccess('Project1_Build2');
bsNocks.buildFailed('Project2_SubProject_Build3');
var light1 = nock(config.hueServerUrl + config.hueUserName).put('/lights/1/state', hue.colors['red']).reply(200);
var light2 = nock(config.hueServerUrl + config.hueUserName).put('/lights/2/state', hue.colors['green']).reply(200);
var light3 = nock(config.hueServerUrl + config.hueUserName).put('/lights/3/state', hue.colors['green']).reply(200);
// Act
app.start();
// Assert
setTimeout(function() {
t.is(light1.done())
t.is(light2.done())
t.is(light3.done())
app.close();
t.end();
}, 1200);
});
In the development.js config file I set the cron-schedule to every second, so it tests very fast. I moved the actual configs to production.js. This configuration setup is described in this blog post.
lightsUpdateSchedule: '*/1 * * * * *'
I have verified it works as expected by changing some internal function names like scheduleOn
to jobOn
and see that the test does fail.
Although it works, I am not yet convinced this is the best and most readable solution. Still open for other answers or idea's.
Explore related questions
See similar questions with these tags.