sinon. spy, stub and mock of JS

sinon
As you know, in Java unit testing, when you can't get the actual object, we can use Mock/Stub to mock our code, which is more convenient for us to test.

EasyMock, JMock and Mockito can solve these problems. The concepts of spy, stub and mock are introduced.
Similarly, we will encounter similar situations in front-end testing. Sinon.js is an excellent library to help us do these things.

Official website: http://sinonjs.org/

Spy
It translates to "surveillance", which is very appropriate.
sinon. spy in JS is mainly used to monitor function calls. Sine wrap s the functions to be monitored. Therefore, it can clearly know how many times the function has been called, what parameters are passed in, what results are returned, and even the exceptions thrown.
Take an example:
 

var sinon = require('sinon');
var expect = require('chai').expect;
 
var orginObj = {
	'launch': function() {
		console.log('I am launch function');
	}
}
 
var myspy = sinon.spy(orginObj, 'launch');// Monitor orginobj launch.
 
console.log(typeof myspy);
 
 
console.log('-------------')
// Call orginobj launch
orginObj.launch('sss');
 
// Number of function calls
expect(orginObj.launch.callCount).to.be.equal(1)
 
// The function was called with this parameter
expect(orginObj.launch.called).be.True;
 
// The function was called once with this parameter
expect(orginObj.launch.withArgs("sss").calledOnce).to.be.True;

As can be seen from the above example, spy does give a detailed description of the function being called. So what is spy? First, it's a function.

var myspy = sinon.spy(orginObj, 'launch');// Monitor orginobj launch.
console.log(typeof myspy);// function

Print this function object as follows:

{ [Function: proxy]
  isSinonProxy: true,
  formatters:
   { c: [Function],
     n: [Function],
     D: [Function],
     C: [Function],
     t: [Function],
     '*': [Function] },
  reset: [Function],
  invoke: [Function: invoke],
  named: [Function: named],
  getCall: [Function: getCall],
  getCalls: [Function],
  calledBefore: [Function: calledBefore],
  calledAfter: [Function: calledAfter],
  calledImmediatelyBefore: [Function: calledImmediatelyBefore],
  calledImmediatelyAfter: [Function: calledImmediatelyAfter],
  withArgs: [Function],
  matchingFakes: [Function],
  matches: [Function],
  printf: [Function],
  calledOn: [Function],
  alwaysCalledOn: [Function],
  calledWith: [Function],
  calledWithMatch: [Function],
  alwaysCalledWith: [Function],
  alwaysCalledWithMatch: [Function],
  calledWithExactly: [Function],
  alwaysCalledWithExactly: [Function],
  neverCalledWith: [Function],
  neverCalledWithMatch: [Function],
  threw: [Function],
  alwaysThrew: [Function],
  returned: [Function],
  alwaysReturned: [Function],
  calledWithNew: [Function],
  alwaysCalledWithNew: [Function],
  callArg: [Function],
  callArgWith: [Function],
  callArgOn: [Function],
  callArgOnWith: [Function],
  throwArg: [Function],
  yield: [Function],
  invokeCallback: [Function],
  yieldOn: [Function],
  yieldTo: [Function],
  yieldToOn: [Function],
  spyCall: { [Function: createSpyCall] toString: [Function] },
  called: false,
  notCalled: true,
  calledOnce: false,
  calledTwice: false,
  calledThrice: false,
  callCount: 0,
  firstCall: null,
  secondCall: null,
  thirdCall: null,
  lastCall: null,
  args: [],
  returnValues: [],
  thisValues: [],
  exceptions: [],
  callIds: [],
  errorsWithCallStack: [],
  displayName: 'launch',
  toString: [Function: toString],
  instantiateFake: [Function: create],
  id: 'spy#0',
  stackTrace: 'Error: Stack Trace for original\n  XXXXXXXXXXXXXX,
  restore: { [Function] sinon: true },
  wrappedMethod: [Function] }

It contains some basic functions, function calls and stackTrace.

stub
In Junit, we sometimes use stubs to embed or directly replace some code to achieve the purpose of isolation. A Stub can simulate the unit test with minimal dependency methods. For example, one method may rely on the execution of another method, which is transparent to us. A good practice is to isolate and replace it with a Stub. This enables more accurate unit testing.
Simply put, a stub is part of the code. Replace the real code with stub at run time, ignoring the original implementation of the calling code. The goal is to test a part of the code independently by replacing a complex behavior with a simpler behavior.
This can be seen from the following examples:

// //Create a stub
var stub = sinon.stub();
 
var testObj = {
	'fun' : function (arg) {
		// ...
	},
	'secondFun' : function(arg){
 
	},
	'thirdFun': function (arg) {
		// body...
	}
}
 
// //Set testobj Replace fun with a stub. After using it, you need to call stub Restore() or testobj fun. Restore restore.
var stub = sinon.stub(testObj, 'fun');
 
// //Set testobj Replace fun with the specified function
// var stub = sinon.stub(testObj, "fun", function (argument) {
// 	// body...
// })
stub();
expect(stub.callCount).to.be.equal(1);
 
 
// testObj. Replace secondfund with the specified function. It is not recommended to use it and will be discarded in the future
var stub2 = sinon.stub(testObj, "secondFun", function (arg) {
	console.log('I am replaced function');
});
 
stub2('arg');
expect(stub2.withArgs("arg").calledOnce).to.be.True;
 
 
var stub3 = sinon.stub(testObj, "thirdFun").callsFake(function (arg) {
	console.log('I am replaced function3');
});
 
stub3('arg');
expect(stub3.withArgs("arg").calledOnce).to.be.True;

Here are two points to note:

1. We need to replace a function. When we replace an undefined function, an error will be reported.

2. A function attribute of an object can only be stubbed once. An error will be reported when trying the second stub.

If you want to stub the same function multiple times in a test set, you can initialize it in the hook function. In addition, because stub replaces the existing function with the specified function, it is best to restore it after each use. The procedure is simple as follows:

stub3.restore()

 

In addition, stub can also be used to change the behavior of functions to complete our special test cases. For example, return value, throw exception, etc. Interested students can try it by themselves.

// stub.returns(6666);
// stub(); // stub() always returns 6666
 
// stub.throws('6666');
 
// stub(); // stub() always throws' 6666 '
 
// stub.withArgs(1).returns(6666);
// stub(1); // stub() always returns 6666

 mock

After an object is mock, we can set our expectations for it, such as how many times we expect it to be called at most and at least, whether to throw an exception, etc.
Mock does not implement any logic, and all calls to mock are false. It is more used to test whether the object interaction behavior has occurred and whether the object interaction has been carried out as expected.

var obj = {
	'fun': function (argument) {
		// body...
	}
 
};
 
var mock = sinon.mock(obj);
 
 
// obj.fun(10) has been called at least once
mock.expects('fun').atLeast(1).atMost(5).withArgs(10);
// mock.expects("method").once().throws();
 
obj.fun(10);
 
mock.verify();// Test whether obj at this time meets the above mock setting conditions.
 
mock.restore();

compare
spy: monitor function calls.
stub: replaces the function behavior of the object.
mock: set the function behavior and verify it.
From this point of view, mock is more like a combination of spy and stub. Look at the following example. Here I want to test myPrinter, but its function has not been implemented. First, replace it with an object.

var orgin = {
	'print': function (prt) {
		console.log('I am print of orgin')
	}
}
 
var myPrinter = {
	'getPrinter': function () {
		// TODO
	}
}
 
var mocker = sinon.mock(orgin);
 
// Since our printer has not been implemented, we use orgin instead So we can test myPrinter's method
var stub4Printer = sinon.stub(myPrinter, "getPrinter").callsFake(function () {
	return orgin;
});
 
// At this time, the test has not been executed, but we expect that the print() of orgin will be called once by passing in 'Hustzw' as a parameter
mocker.expects('print').once().withArgs('Hustzw');
 
// Test execution
stub4Printer().print('Hustzw');
 
mocker.verify();// Verify that the mocker meets the above mock expectations.
// After the execution of the test, our print() should be called once during the test
 
mocker.restore();

Keywords: Javascript ECMAScript

Added by gokhul on Tue, 28 Dec 2021 12:29:23 +0200