6.9 Test Spies


Create app.js and db.js.


Contrive fake db.js

module.exports.saveUser = (user) => console.log('saving the user', user);

Contrive fake app.js

const db = require('./db.js');
module.exports.handleSignup = (email, password) => {
db.saveUser({
email,
password
});
};

Spies

toHaveBeenCalled

const expect = require('expect');
describe('App', ()=>{
it('should call the cpy correctly', ()=>{
var spy = expect.createSpy();
spy();
expect(spy).toHaveBeenCalled();
})
})

This test would pass because spy has been called.


toHaveBeenCalledWith

spy('Jude', 19);
expect(spy).toHaveBeenCalledWith('Jude', 19);

This would pass, because spy indeed has been called with ‘Jude’ and 19.


Install rewire by npm install rewire --save-dev

The concept of spies test can be a bit confusing at the first time, so I will spend more time on this article.


  1. What is a spy? A spy is a function returned from expect.createSpy(). This is for expect([spy_function]).toHaveBeenCalledWith.

  2. Why do we need rewire? rewire('./module') does the same job as require('./module') but it provides two functions : module.__set__ and module.__get__. We focus on the former one. When we call module.__set__('AFunction', spyFunction), the AFunction (can be object too) will be injected by your spyFunction.

  3. How does the test work? Since the function of interest has been injected by our spyFunction, everything passing into that function can be checked with expect(spyFunction).toHaveBeenCalledWith([ArgumentLists]). The spying function normally would be with side effects. For example, we will see a DB module. We cannot easily test DB module because everything we call will be written into the DB. Thus, we use this technique.


Example:

const expect = require('expect');
const rewire = require('rewire');
var app = rewire('./app'); // same as require('app') but we can inject function/object
describe('App', ()=>{
// an object to be injected into `const require('./db')` as shown in the app.js
var db = {
saveUser: expect.createSpy() // this creates a spy such that we can check the arguments called
};
app.__set__('db', db); // we are injecting the object db... originally db was imported from db.js but now db became an object with spies
it('should call saveUser with user object', ()=>{
var email = "gaojude@outlook.com";
var password = "123456abc";
app.handleSignup(email, password);
expect(db.saveUser).toHaveBeenCalledWith({email, password});
});
})