Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

td.replace support for constructors? #502

Closed
3 of 13 tasks
haihovu opened this issue Feb 2, 2023 · 2 comments
Closed
3 of 13 tasks

td.replace support for constructors? #502

haihovu opened this issue Feb 2, 2023 · 2 comments

Comments

@haihovu
Copy link

haihovu commented Feb 2, 2023

Description

My needs arose from wanting to test a function that internally created some objects from 3rd party libraries. I want to intercept calls to the constructors of classes in those 3rd party libraries so that I can a) verify that the function being tested actually instantiates those objects with the desired parameters, and b) return fake/mock instances of those objects for testability. I thought that td.replace() is a perfect candidate for this, eg. td.replace(Some3rdPartyClass.prototype, 'constructor'); this worked perfectly with any other methods of classes, I can mock pretty much all methods, static as well as instance, except for 'constructor'. Maybe this was not meant to be but I can't find any documentation that state otherwise.

Issue

I tried to replace the constructors like so:

import { SomeClass } from 'some-library';
import * as td from 'testdouble';
const someMockInstance = {};
const constructor = td.replace(SomeClass.prototype, 'constructor');
td.when(constructor()).thenReturn(someMockInstance);
const instance =  new SomeClass();
expect(instance).to.equal(someMockInstance);

This will never pass, and when I ran the debugger, new SomeClass() always invoked the real constructor and not my mock constructor.

Maybe what I tried to do is misguided as there are not many who asked about this out in the wild.

Environment

  • node -v output: v18.12.1
  • npm -v (or yarn --version) output: 8.19.2
  • npm ls testdouble (or yarn list testdouble) version: testdouble@3.16.8

Failing Test

  • Fork the repo
  • Add a failing test (probably to the `/regression/src' directory)
  • Submit a pull request for the failing test or link to your branch here

Example Repo

  • Create a minimal repository that reproduces the issue
  • Make sure that a fresh clone can run only npm it and observe the issue
  • Link to that repo here

Runkit Notebook

  • Create a Runkit notebook
  • Invoke var td = require('testdouble') at the top
  • Verify the behavior your issue is concerned with by clicking "Run"
  • Link to the Runkit here

Code-fenced Examples

@searls
Copy link
Member

searls commented Feb 12, 2023

I suspect any faked constructors may need to be manually replaced instead of relying on automatic replacement, but I'm not sure.

Here's our documentation on fake constructors: https://github.com/testdouble/testdouble.js/blob/main/docs/4-creating-test-doubles.md#tdconstructor

And here's an example from the tests: https://github.com/testdouble/testdouble.js/blob/65d7023b2f1c9084cdd7cd288550aa4ab68495bf/test/safe/constructor.test.js

@searls searls closed this as completed Feb 12, 2023
@haihovu
Copy link
Author

haihovu commented Feb 27, 2023

td.contructor() only gives you a fake constructor with which to instantiate mock objects of the target classes, it doesn't replace the constructors of those classes such that when the code under test steps on their constructors that you are notified with td.when() and td.verify(). I did find a solution, assuming there is a package called abc that defines a class Abc, I can hijack the constructor of class Abc as follows: const constructorAbc = td.replace(require('abc'), 'Abc'). Then td.when(constructorAbc(..)).then...() or td.verify(constructorAbc(...)) will be triggered when someone steps on that constructor. That was what I wanted to do but I couldn't find any doc on that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants