SkillAgentSearch skills...

Chadojs

the way of tdd

Install / Use

/learn @robindanzinger/Chadojs
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Build Status

chadojs

chadojs is a mocking library for nodejs which reduces the need for integration tests. Instead of integration tests it supports writing verification tests.

production ready?

chadojs is more a proof of concept than a production ready library. Although it is used in some productive environments. Unfortunately I don't have enough time to add new interesting features. Still I think the basic concept is worth to have a look at. Especially the example how software can be implemented with chadojs and outside-in tdd. I hope that some day it will be enough to focus on the necessary unit tests and still guarantee the basic correctness of the application. If you have any issues or questions about chadojs, outside-in tdd and mocking, don't hesitate to ask me. Thank you!

why a new mocking library

The problem with mocks is, that the tests still pass, although the real objects might not work together any more.

When I first started to learn tdd in javascript, I used mocks extensively. Whenever I renamed a unit or function, the unit tests didn't break (because they were unit-tests :-) ). But when I ran the application it crashed because the units didn't work together anymore.

When you mock collaborating objects, you can't be sure, that the real object behaves like you expected.

There are two ways how you can deal with that.

  • Avoid mocks and use the real object instead
  • Assure that mocks are in sync with the real object

avoiding mocks

Avoiding mocks is not always possible or desired: E.g. the real object is a 3rd party service, it's too slow, or might not always work (e.g. webservices, database-access). Or you want to test a specific behavior (which might depend on the time, week-day, or that a specific service is / is not available). Or in outside-in tdd the collaborating object doesn't yet exist.

integration tests

Integration tests are often used to check, whether the real objects work together.

But integration tests are hard to setup, might be slow and we can't test specific behavior which depends on a specific state (e.g. time, service-availability, failure,...). Integration tests might fail not only because of a bug but because of a depending resource which is not available for a moment.

verification tests

chadojs tries to address this problem with additional verification tests. With chadojs you do not need to write these types of integration tests. Instead whenever you mock a unit, you verify that the real object can behave like the mock.

install chadojs

npm install chado --save-dev

setup chadojs

mocha

Create a new file (e.g.: mocha-chado.js) in your test directory and add the following lines.

var chado = require('chado');
var fs = require('fs');
after(function () {
  chado.consoleReporter.logReport();
  fs.writeFileSync(
    "chado-result.json", 
    JSON.stringify(chado.repo, null, 2)
  );
});

Create a new reporter file (chado-reporter.js) with following content and add it to the mocha.opts file.

var Spec = require('mocha').reporters.Spec;
var setCurrentTest = require('chado').setCurrentTest;
function Reporter(runner) {
  runner.on('test', function (test) {
    setCurrentTest(test.title);
  });
  return new Spec(runner);
}
module.exports = Reporter;

busterjs

Create a new file (e.g.: buster-chado.js) in your test directory and add the following lines.

var chado = require('chado');
var fs = require('fs');
var testRunner = require('buster').testRunner;
testRunner.on('suite:end', function () {
  chado.consoleReporter.logReport();
  fs.writeFileSync(
    "chado-result.json", 
    JSON.stringify(chado.repo, null, 2)
  );
});

jasmine

Create a new SpecHelper file and add the following lines.

var chado = require('chado');
var fs = require('fs');
var chadoJasmineReporter = {
  specStarted: function (result) {
    chado.setCurrentTest(result.fullName);
  },
  specDone : function (result) {
    chado.setCurrentTest(null);
  }
};
jasmine.getEnv().addReporter(chadoJasmineReporter);
afterAll(function() {
  chado.consoleReporter.logReport();
  fs.writeFileSync(
    "chado-result.json",
    JSON.stringify(chado.repo, null, 2)
  );
});

other testrunners

Ensure that after the test suite ran chado.consoleReporter.logReport() is called and if you want to use the html-reporter that the chado.repo is written to a file. Depending on the test framework ensure also, that the testname will be set in chado.

chado.setCurrentTest(testname);

how does it work

chadojs uses an assume-verify-approach<br> When you mock a function in chadojs you make an assumption about how the real object behaves. On the other hand you have to verify that the real object can behave like you assumed.

If you forget to verify an assumption, chadojs reminds you, that there might be a problem.<br> If you verify an assumption, but you forgot to make the assumption, chadojs reminds you, that you might not have tested all necessary use cases.

create a mock

var createDouble = require('chado').createDouble;
var myTestdouble = createDouble('collaboratorName');
var partialMock = createDouble('collaboratorName', realCollabortor);

define an assumption

var chado = require('chado');
var collaborator = chado.createDouble('collaboratorName');
var assume = chado.assume;

assume function returns a value

assume(collaborator).canHandle('foo').andReturns('bar');
// collaborator.foo() === 'bar'
assume(collaborator).canHandle('foo').withArgs('argument').andReturns('bar');
// collaborator.foo('argument') === 'bar'
assume(collaborator).canHandle('foo').withArgs('first', 2, 'third').andReturns('bar');
// collaborator.foo('first', 2, 'third) === 'bar'

assume function throws an error

assume(collaborator).canHandle('foo').andThrowsError('error message');
// collaborator.foo() -> Error: error message
assume(collaborator).canHandle('foo').withArgs('argument').andThrowsError('error message');
// collaborator.foo('argument') -> Error: error message

assume function calls a given callback

var callback = chado.callback
// var realCallback = function (result) {console.log(result);};

assume(collaborator).canHandle('foo').withArgs(callback).andCallsCallbackWith();
// collaborator.foo(realCallback) -> console: undefined
assume(collaborator).canHandle('foo').withArgs(callback).andCallsCallbackWith('bar');
// collaborator.foo(realCallback) -> console: 'bar'
assume(collaborator).canHandle('foo').withArgs(callback, 'argument').andCallsCallbackWith('bar');
// collaborator.foo('argument', realCallback) -> Error
// collaborator.foo(realCallback, 'argument') -> console: 'bar'
assume(collaborator).canHandle('foo').withArgs('argument', callback).andCallsCallbackWith('bar');
// collaborator.foo('argument', realCallback) -> console: 'bar'
// collaborator.foo(realCallback, 'argument') -> Error

define a verification

var chado = require('chado');
var collaborator = chado.createDouble('collaboratorName');
var verify = chado.verify;

verify function returns a value

var collaborator = {foo:function () { return 'bar';}};
// ok
verify('collaboratorName').canHandle('foo').andReturns('bar').on(collaborator);
verify('collaboratorName').canHandle('foo').withArgs('argument').andReturns('bar').on(collaborator);
verify('collaboratorName').canHandle('foo').withArgs('first', 2, 'third').andReturns('bar').on(collaborator);
// error
verify('collaboratorName').canHandle('foo').andReturns('foo').on(collaborator); // because bar != foo
verify('collaboratorName').canHandle('foo').withArgs('arg').andReturns('bar').on(collaborator); // because argument != arg
verify('collaboratorName').canHandle('foo').withArgs(1, 2, 'third').andReturns('bar').on(collaborator); // because 1 != first

verify function throws an error

var collaborator = {foo: function () { throw Error('error message');}};
// ok: 
verify('collaboratorName').canHandle('foo').andThrowsError('error message').on(collaborator);
// error:
verify('collaboratorName').canHandle('bang').andThrowsError('error message').on(collaborator);
// ok
verify('collaboratorName').canHandle('foo').withArgs('argument').andThrowsError('error message').on(collaborator);

verify function calls a given callback

var callback = chado.callback; // this function is never called in verify!
var collaborator = {foo: function (callback) {callback();};

// ok
verify('collaboratorName').canHandle('foo').withArgs(callback).andCallsCallbackWith().
  on(collaborator, function () {});    

// throws error, because collaborator.foo doesn't call callback with argument 'bar'
verify('collaboratorName').canHandle('foo').withArgs(callback).andCallsCallbackWith('bar').
  on(collaborator, function () {});    

// ok
collaborator = {foo: function (callback) {callback('bar');};
verify('collaboratorName').canHandle('foo').withArgs(callback).andCallsCallbackWith('bar').
  on(collaborator, function () {});    

// throws error, because collaborator.foo uses first argument as callback
verify('collaboratorName').canHandle('foo').withArgs('argument', callback).andCallsCallbackWith('bar').
  on(collaborator, function () {});    
  
// ok
collaborator = {foo: function (argument, callback) {callback('bar');};
verify('collaboratorName').canHandle('foo').withArgs('argument', callback).andCallsCallbackWith('bar').
  on(collaborator, function () {});    

evaluate assumptions and verifications

manual evaluation with chado.analyzer
var chado = require('chado');
// chado.repo is the repository and contains all assumptions and verifications
var repo = chado.repo;
// chado.analyzer contains functions for analyzing the assumptions and verifications
var analyzer = chado.analyzer;
// first we have to convert the repository to an array
var reportArray = analyzer.read(chado.repo);

// now we 

Related Skills

View on GitHub
GitHub Stars11
CategoryDevelopment
Updated5mo ago
Forks1

Languages

JavaScript

Security Score

67/100

Audited on Oct 30, 2025

No findings