0

Having the following balanceRepository.js file.

module.exports = function(){
this.getBalance = (accountId)=>{
    throw 'NotImplementedException';
};};

And the service transactionService.js file.

module.exports = function(balanceRepo){
this.isTransactionAllowed = (amount, accountId)=>{
    const balance = balanceRepo.getBalance(accountId);
    return amount <= balance;
};};

I would like to unit test that when we call transactionService.isTransactionAllowed, it will call internally to balanceRepository.getBalance. I was expecting the following code to work.

let BalanceRepository = require('../balanceRepository');
let TransactionService = require('../transactionService');

let should = require('should');
let sinon = require('sinon');

describe('transaction service', ()=>{
   let fakeBalanceRepo = sinon.spy(BalanceRepository);
   let transactionSrv = new TransactionService(fakeBalanceRepo);

   transactionSrv.isTransactionAllowed(100, 987654321);

   it('should call getBalance', ()=>{
      fakeBalanceRepo.getBalance.should.be.called();
});});

I create a spy of the class BalanceRepository. I inject that spy object to my real implementation of TransactionService. Then, I assert that the getBalance method is called on the spy. It makes sense to me but it is not working!!

How can I inject a fake and assert that one of its methods was called?

osotorrio
  • 960
  • 1
  • 11
  • 30

2 Answers2

1

sinon.spy(BalanceRepository) doesn't mean that class methods will be automatically spied (they won't).

TransactionService expects BalanceRepository instance while fakeBalanceRepo is a constructor.

If the intention is to spy on class method and not change its behaviour, it's:

   let balanceRepo = new BalanceRepository;
   sinon.spy(balanceRepo, 'isTransactionAllowed')
   let transactionSrv = new TransactionService(balanceRepo);
   transactionSrv.isTransactionAllowed(100, 987654321);
   balanceRepo.getBalance.should.be.called();
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • I did tried that but it would not work either. If we inject to TransactionService the real implementation of BalanceRepository, it will call the real getBalance throwing 'NotImplementedException'. In a real scenario would try to connect to a database. It is obvious that we would not want that in a unit test. Am I missing something? – osotorrio Sep 20 '18 at 03:39
  • 1
    It wasn't clear what was the problem with original code. Yes, stubbing DB calls is desirable in unit tests. If you need to mock the implementation then use `sinon.stub`, not `sinon.spy`. – Estus Flask Sep 20 '18 at 03:50
0

This works as estus has commented.

let balanceRepo = new BalanceRepository();
let fakeGetBalance = sinon.stub(balanceRepo, 'getBalance');

let transactionSrv = new TransactionService(balanceRepo);
transactionSrv.isTransactionAllowed(100, 987654321);

it('should call getBalance', ()=>{
    fakeGetBalance.should.be.called();
});
osotorrio
  • 960
  • 1
  • 11
  • 30