testdouble / testdouble.js

A minimal test double library for TDD with JavaScript
MIT License
1.42k stars 142 forks source link

How to call-through original function? #512

Closed aytekin-smartcar closed 1 year ago

aytekin-smartcar commented 1 year ago

Description

I want to spy and call through original function.

Issue

I am getting RangeError: Maximum call stack size exceeded

Environment

Failing Test

Example Repo

Runkit Notebook

Code-fenced Examples

import * as td from 'testdouble';

const myObj = {
  myFunc(arg1, arg2) {
    console.log(`I am executed with: ${arg1} and ${arg2}`);
  }
};

// Create a test double for `myObj.myFunc`
const myFunc = td.replace(myObj, 'myFunc');

// Call through the test double and execute the original `myObj.myFunc` method
td.when(myFunc('foo', 'bar')).thenDo((...args) => myObj.myFunc(...args));

// Call the code that uses `myObj.myFunc`
myObj.myFunc('foo', 'bar');

// Verify that `myObj.myFunc` was called once with the specified arguments
td.verify(myObj.myFunc('foo', 'bar'), { times: 1 });

console.log('ALL TESTS PASSED!');

I would like to spy on an existing function and also execute it. Specifically, I want to run

console.log(`I am executed with: ${arg1} and ${arg2}`);

In some other libraries, this can be done with a callThrough method, but I have been unable to do so here. Instead, I am receiving a RangeError: Maximum call stack size exceeded.

How can I resolve this issue?

searls commented 1 year ago

td.replace overwrites the reference until td.reset is called. If you wanted to do this for whatever reason, you'd have to grab a reference to the function you're overwriting (see ogFunc = … below).

Note that stubbing this interaction and then verifying it was called is a separate issue that triggers a warning in testdouble.js because it's a common test smell with mocking

const td = require('testdouble');

const myObj = {
  myFunc(arg1, arg2) {
    console.log(`I am executed with: ${arg1} and ${arg2}`);
  }
};

const ogFunc = myObj.myFunc;
// Overwrite myObj.myFunc with a test double
const myFunc = td.replace(myObj, 'myFunc');

// Call through the test double and execute the original `myObj.myFunc` method
td.when(myFunc('foo', 'bar')).thenDo((...args) => ogFunc(...args));

// Call the code that uses `myObj.myFunc`
myObj.myFunc('foo', 'bar');

// Verify that `myObj.myFunc` was called once with the specified arguments
td.verify(myObj.myFunc('foo', 'bar'), { times: 1 });

// Restore myObj.myFunc
td.reset();

console.log('ALL TESTS PASSED!');