How To Mock a Dependency Class in Typescript Node Using Chai-Spies - node.js

Given something like my-module.ts that will be called by an external consumer that of course cannot do dependency injection to this module's constructor:
import Dependency from 'dependency-class';
import {myDataFormat} from '../types/my-module-types';
export default class MyModule {
private dependency: Dependency;
constructor() {
this.dependency = new Dependency();
}
getMyData(): myDataFormat {
const dependencyData = this.dependency.getDependencyData();
// parse/process data
// return modified data
}
}
How can we test that Dependency.getDependencyData() was called by MyModule.getMyData()... using only mocha, chai, and chai-spies?

my-module.test.ts:
import Dependency from 'dependency';
import MyModule from '../src/modules/my-module'
import 'mocha';
import chai = require('chai');
chai.should(); //enable chai should()
import spies = require('chai-spies');
chai.use(spies); //extend chai with spies plugin
describe('Tests.MyModule', () => {
let instance: MyModule | undefined;
before(() => {
// Create spies/stubs/mocks as needed
chai.spy.on(Dependency.prototype, ['getDependencyData'], () => {
// this is replacement function body if we want to test specific return data
});
// Create MyModule instance
instance = new MyModule();
});
after(() => {
// Reset MyModule instance
instance = undefined;
// Remove spies/stubs/mocks that were created
chai.spy.restore();
});
describe('getMyData()', () => {
it('should call Dependency.getDependencyData()', () => {
// Arrange
// - Set up spies/stubs/mocks (done above)
// - Create a MyModule instance (done above)
// Act
instance.getMyData();
// Assert
Dependency.prototype.getDependencyData.should.have.been.called();
});
});
});
Versions in Node package.json:
"#types/chai": "^4.3.0",
"#types/chai-spies": "^1.0.3",
"#types/mocha": "^9.1.0",
"chai": "^4.3.6",
"chai-spies": "^1.0.0",
"mocha": "^9.2.1",

Related

can't get the Jest provided ESM example to run

I'm just trying to get the ES6 Class Mocks example provided by Jest to run green.
here's my code repo
it's taken me way to long to even get to this point, but the tests still fail with
TypeError: SoundPlayer.mockClear is not a function
system under test
import SoundPlayer from './sound-player';
export default class SoundPlayerConsumer {
constructor() {
this.soundPlayer = new SoundPlayer();
}
playSomethingCool() {
const coolSoundFileName = 'song.mp3';
this.soundPlayer.playSoundFile(coolSoundFileName);
}
}
the test
import {jest} from '#jest/globals';
import SoundPlayer from './sound-player';
import SoundPlayerConsumer from './sound-player-consumer';
const mockPlaySoundFile = jest.fn();
jest.mock('./sound-player', () => {
return jest.fn().mockImplementation(() => {
return {playSoundFile: mockPlaySoundFile};
});
});
beforeEach(() => {
SoundPlayer.mockClear();
mockPlaySoundFile.mockClear();
});
it('The consumer should be able to call new() on SoundPlayer', () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
// Ensure constructor created the object:
expect(soundPlayerConsumer).toBeTruthy();
});
it('We can check if the consumer called the class constructor', () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
expect(SoundPlayer).toHaveBeenCalledTimes(1);
});
it('We can check if the consumer called a method on the class instance', () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
const coolSoundFileName = 'song.mp3';
soundPlayerConsumer.playSomethingCool();
expect(mockPlaySoundFile.mock.calls[0][0]).toEqual(coolSoundFileName);
});
system under test dependency
export default class SoundPlayer {
constructor() {
this.foo = 'bar';
}
playSoundFile(fileName) {
console.log('Playing sound file ' + fileName);
}
}

How to test a function in React Component with Jest + Enzyme?

I am using the testing-library/jest-dom + Enzyme to do the unit testing.and here I need to test a funtion inside a React function.
import React, { useEffect } from 'react';
function Component1() {
useEffect( async () => {
fetchSomeData();
}, [])
const fetchSomeData = async () =>{
console.log('fetchSomeData')
}
return <div>Component1</div>;
}
export default Component1;
and my test file:
Enzyme.configure({adapter:new Adapter()})
describe('Name of the group', () => {
it('should ', () => {
const wrapper = shallow( <Component1/>)
expect(wrapper.exists());
});
});
in fact I don't need to test the correctness , I just need the code be covered in jest. but whatever I tried , it seems not covered by jest:

Type Error on Transpiling Babel class.default is not a constructor

'use strict';
/**
* PortalHTML.js
*/
import SingleContextHTML from './SingleContextHTML';
import MultiContextHTML from './MultiContextHTML';
export default class PortalHTML{
constructor (type) {
switch(type) {
case "1":
this.portalStrategy = new SingleContextHTML();
break;
default:
this.portalStrategy = new MultiContextHTML();
break;
}
}
render (...args) {
this.portalStrategy.renderHTML(args);
}
_strategyPeak(){
return this.portalStrategy.constructor.name;
}
}
/**
* SingleContextHTML.js
*/
'use strict';
import PortalHTMLStrategy from './PortalHTMLStrategy';
export default class SingleContextHTML extends PortalHTMLStrategy {
constructor(){
super()
}
renderHTML(args){}
}
/**
* Multi.js (same as single) ^^ above
*/
/**
* PortalHTMLStrategy.js
*/
'use strict';
export default class PortalHTMLStrategy{
constructor(){}
renderHTML(args){}
}
/**
* Tester Mocha and Chai
*/
'use strict';
import PortalHTML from "./PortalHTML";
import chai from 'chai';
let basicPortalHTMLTest = () => {
let singleContextHTML = new PortalHTML("1");
let multiContextHTML = new PortalHTML("multi");
describe('PortalHTML: Initialization of PortalHTML', () => {
it('Should be an instance of class SingleContextHTML', (done) => {
chai.expect(singleContextHTML._strategyPeak()).to.equal('SingleContextHTML');
done();
});
it('Should be an instance of MultiContextHTML', (done) => {
chai.expect(singleContextHTML._strategyPeak()).to.equal('MultiContextHTML');
done();
});
});
};
basicPortalHTMLTest();
I am trying to test an implementation of a strategy pattern, but I am running into an error that states the following after I run my test script with mocha.
var singleContextHTML = new _PortalHTML2.default("1");
^
TypeError: _PortalHTML2.default is not a constructor
I am using node6 and the following babel packages:
"babel-cli": "^6.14.0",
"babel-core": "^6.17.0",
"babel-loader": "^6.2.4",
"babel-preset-es2015": "^6.16.0",
"babel-preset-react": "^6.11.1",
"babel-preset-stage-2": "^6.11.0"
I have been trying to debug this for around half a day. If anyone sees anything immediately wrong that I am doing I would be extremely grateful.
I was using the incorrect babel preset. Switching over to es2016 or above fixed this error.

Why is my module not appearing in require.cache?

OS: Windows 10
Node version: 0.10.36
Mocha global version: 1.21.4
I'm attempting to use mocha to unit-test my code, but a local variable inside the code I'm trying to test is persisting between tests, causing problems.
When I look inside require.cache, between tests, I don't see my module in there. It is my understanding that I should be clearing the cache if I want to reset this module between tests.
I made a small node project to demonstrate this issue:
package.js:
{
"name": "cache-test",
"version": "0.0.1",
"dependencies": {
"lodash": "4.5.0"
},
"devDependencies": {
"chai": "1.9.2",
"mocha": "1.21.4",
"mockery": "1.4.0",
"sinon": "1.10.3",
"app-root-path":"*"
}
}
module.js:
var foo = "default value";
exports.init = function(){
foo = 'init';
}
exports.returnFoo = function(){
return foo;
}
test/test-module.js
var chai = require("chai"),
expect = chai.expect,
mockery = require("mockery"),
appRoot = require('app-root-path');
var module;
describe("module", function () {
before(function () {
mockery.enable({ useCleanCache: true });
});
beforeEach(function () {
mockery.registerAllowable(appRoot + "/module", true);
module = require(appRoot + "/module");
});
afterEach(function () {
console.log('deleting', require.cache[require.resolve(appRoot + "/module")]);
delete require.cache[require.resolve(appRoot + "/module")];
module = null;
mockery.deregisterAll();
});
after(function () {
mockery.disable();
});
describe("test",function(){
it("foo should be 'init' after running init()",function(){
module.init();
console.log('foo is ',module.returnFoo());
expect(module.returnFoo()).to.equal('init');
});
it("foo should be 'default value' if init() is not run",function(){
console.log('foo is ',module.returnFoo());
expect(module.returnFoo()).to.equal("default value");
});
});
});
running mocha prints
module
test
foo is init
√ foo should be 'init' after running init()
deleting undefined
foo is init
1 failing
Oh, I needed to add
mockery.resetCache() to my afterEach function. That solved it.
It seems like the useCleanCache option, and deleting the entry from require.cache aren't compatible with each-other, as the former keeps it from appearing in the latter.
So it's either:
Don't use useCleanCache
Delete it "manually" from require.cache
OR
Use useCleanCache
Use resetCache()
but don't attempt to mix and match.

Is there a trick to using Mockery in Mocha test with Typescript?

It would seem the usual method of importing in typescript prevents the modules from being mocked... Assume I have the following product code in a node.js project written in typescript that I would like to test:
// host.ts
import http = require('http');
export class Host {
public start(port: number): http.Server {
return http.createServer().listen(port);
}
}
I have the below unit test using mockery (d.ts in pull request #3313) and mocha:
import chai = require('chai');
import mockery = require('mockery');
import webserver = require('../hosting/host');
describe('host', (): void => {
describe('start()', (): void => {
before(() : void => {
mockery.enable();
});
after((): void => {
mockery.deregisterAll();
mockery.disable();
});
it('should create an http server', (): void => {
mockery.registerMock('http', {
Server: mocks.Server,
createServer: (app: any) : any => new mocks.Server(app)
});
var host: webserver.Host = new webserver.Host({ port: 111 });
var server: any = host.start();
chai.expect(server).is.instanceOf(mocks.Server);
});
});
});
module mocks {
'use strict';
export class Server {
app: any;
constructor(app: any) {
this.app = app;
}
}
}
The problem is that when import webserver = require('../hosting/host') is called the mocks in the test aren't setup yet and the un-mocked require('http') is returned. I attempted to try var http = require('http') within the Host.start function, but this prevents http.Server from being declared as a return value.
How should I go about implementing unit tests in Typescript with Mocks? Is there a better library than mockery that would work better?
After all day of scouring the web I finally learned that: Yes, there is a trick to using Mockery in Mocha test with Typescript. The trick is using the typeof identifier to reference the module. I discovered this in the Optional Module Loading and Other Advanced Loading Scenarios in this document.
My updated code now looks like this:
// host.ts
import httpDef = require('http');
export class Host {
public start(port: number): httpDef .Server {
var http: typeof httpDef = require('http');
return http.createServer().listen(port);
}
}
This allows me to set up mocks in my mocha test like this:
import chai = require('chai');
import mockery = require('mockery');
import webserver = require('../hosting/host');
import httpDef = require('http'):
describe('host', (): void => {
describe('start()', (): void => {
before(() : void => {
mockery.enable();
});
after((): void => {
mockery.deregisterAll();
mockery.disable();
});
it('should create an http server', (): void => {
var mockServer: httpDef.Server = <httpDef.Server>{};
var mockHttp: typeof httpDef = <typeof httpDef>{};
mockHttp.createServer = () : httpDef.Server => mockServer;
mockery.registerMock('http', mockHttp);
var host: webserver.Host = new webserver.Host({ port: 111 });
var server: any = host.start();
chai.expect(server).is.equals(mockServer);
});
});
});
Some other scenarios where this can be used for dependency injection can be found here.

Resources