node ts-jest spyOn method does not match overload - node.js

I'm trying to use Jest in conjunction with ts-jest to write unit tests for a nodeJS server. I have something set up very similar to below:
impl.ts
export const dependency = () => {}
index.ts
import { dependency } from './impl.ts';
export { dependency };
consumer.ts
import { dependency } from '../impl' <- importing from index.ts
export const consumer = () => {
try {
dependecy();
return true;
} catch (error) {
return false;
}
}
consumer.test.ts
import * as dependencies from '../impl'
import { consumer } from './consumer'
const mockDependency = jest.spyOn(dependencies, 'depenedncy');
describe('function consumer', function () {
beforeEach(function () {
mockDependency.mockReturnValueOnce(false);
});
test('should return true', () => {});
})
This is just toy code, but the actual export / import / test files follow a similar structure. I'm getting typescript errors along these lines:
TS2769: No overload matches this call.
Specifically, that the method being spied on is not part of the overload of the import for dependencies, so I can't stub it out. I am doing literally the same thing in a different test file and it has no issues. Anyone know how to resolve the typing issue?

The issue turned out to be in the typing of the dependency function itself. The return value typing was incorrect and that was what was resulting in the Typescript error. Essentially I had this:
export const dependency: Handler = () => {
return () => {} <- is of type Handler
}
rather than this
export const dependency = (): Handler => {
return () => {} <- is of type Handler
}
Stupid mistake, hope it helps someone else in the future. My take away is that if you have a type error that doesn't make sense make sure you check the typing of all variables involved.

Related

How to separately type Fastify decorators in different plugins?

I'm having trouble typing decorators in two plugins (scopes):
import Fastify, { FastifyInstance } from 'fastify'
const fastify = Fastify()
// scope A
fastify.register((instance) => {
instance.decorate('utilA', 'AAA')
instance.get('/', (req, reply) => {
const data = instance.utilA // Property 'utilA' does not exist on type 'FastifyInstance<...>'
reply.send(data)
})
}, { prefix: '/A/' })
// scope B
fastify.register((instance) => {
instance.decorate('utilB', () => 'BBB')
instance.get('/', (req, reply) => {
const data = instance.utilB() // Property 'utilB' does not exist on type 'FastifyInstance<...>'
reply.send(data)
})
}, { prefix: '/B/' })
I can define types globally:
declare module 'fastify' {
interface FastifyInstance {
utilA: string
utilB: () => string
}
}
And it solves the compiler errors. But since utilA and utilB are defined globally compiler allows me to use utilB in the scope A and vice versa.
Is there a way to do the same but independently per scope?
No I do not think this is possible with the declare module '' syntax due to the way that TypeScript works (although someone smarter than me might be able to figure out how).
One thing you can do is create a sub type of the FastifyInstance with your new properties (they will need to be optional or TypeScript might get mad at you depending on your tsconfig settings) and type your plugins fastify instance with that type. For example:
// Scope A
import { FastifyInstance } from 'fastify';
type MyPluginsFastifyInstance = FastifyInstance & { myThing?: MyType; }
export default async (fastify: MyPluginFastifyInstance) => {
... // Implementation.
fastify.myThing // TypeScript is happy :)
}
// Scope B
export default async (fastify) => {
fastify.myThing // TypeScript is mad at you >:(
}

How do you mock typeorm's getManager using testdouble?

When creating unit tests for typeorm, I want to mock my connection to the database so that I can run unit tests without actually ever connecting to the DB (a good thing!)
I see places where people have mocked typeorm's repositories using testdouble (which I am also using), but I am trying to do this with getManager and am having an issue figuring out how to make it work.
Here's an example. I have a class that, in the constructor, creates an EntityManager by using getManager() for a connection called 'test':
export class TestClass {
constructor() {
const test: EntityManager = getManager('test');
}
}
Now I want to test that I can simply create this class. Here's a sample (using mocha, chai, and testdouble):
describe('data transformer tests', () => {
it('can create the test class', () => {
// somehow mock getManager here
const testClass: TestClass = new TestClass();
chai.expect(testClass, 'could not create TestClass').to.not.be.null;
});
});
When I try this, I get this error message from typeorm:
ConnectionNotFoundError: Connection "test" was not found.
Here are some of the things I've tried to mock getManager:
td.func(getManager)
same error as above.
td.when(getManager).thenReturn(td.object('EntityMananger'));
gets the message:
Error: testdouble.js - td.when - No test double invocation call detected for `when()`.
Any ideas what the magic sauce is here for mocking getManager?
I tried sinon instead of testdouble. I created a small repository, which shows how you can mock database for your blazing unit-tests :)
I tried to cover all TypeORM test cases using Jest and Mocha
Example
import * as typeorm from 'typeorm'
import { createSandbox, SinonSandbox, createStubInstance } from 'sinon'
import { deepEqual } from 'assert'
class Mock {
sandbox: SinonSandbox
constructor(method: string | any, fakeData: any, args?: any) {
this.sandbox = createSandbox()
if (args) {
this.sandbox.stub(typeorm, method).withArgs(args).returns(fakeData)
} else {
this.sandbox.stub(typeorm, method).returns(fakeData)
}
}
close() {
this.sandbox.restore()
}
}
describe('mocha => typeorm => getManager', () => {
let mock: Mock
it('getAll method passed', async () => {
const fakeManager = createStubInstance(typeorm.EntityManager)
fakeManager.find.resolves([post])
mock = new Mock('getManager', fakeManager)
const result = await postService.getAll()
deepEqual(result, [post])
})
afterEach(() => mock.close())
})

Cannot mock filesystem in nodejs unit tests

Overview
I have a simple module written in nodejs that uses fs-extra package to test if a file exists. The module throws when the path exists and proceed to next procedure otherwise. Here is the source file:
// - main.js -
import fs from 'fs-extra'
export default async (pathName) => {
// Do not proceed if path already exists.
if (await fs.pathExists(projectPath)) {
throw new Error(chalk.red.bold(`${projectPath} already exists`))
}
// more logic here
}
I want to write a unit test that tests the bellow logic:
If filepath exists, we expect to throw an error
I don't want to mess up with the real filesystem -in case my code contains some nasty bug that could destroy it- so I went to an alternative solution, mocking the filesystem using mock-fs. Here is the spec file:
// - main.js spec file -
import mainFunction from '../main'
import mockfs from 'mock-fs'
describe('test main function', () => {
beforeEach(() => {
mockfs({
home: {
user: {
dummy: {}
}
}
})
})
test('expect to throw', async () => {
await mainFunction('/home/user/dummy')
})
afterEach(() => {
mockfs.restore()
})
})
What's the problem?
Every time I run the test, the main function does not throw. This happens because mockfs fake-filesystem was declared in the spec file, so the fs module in main source file does not know for the mockfs fake-filesystem and checks the real one. By the time that I do not have a folder named /home/user/dummy in my real filesystem the check always fails.
Expected behaviour
mainFunction in spec file should throw
Actual behaviour
mainFunction in spec file DOES NOT throw
Other info
I guess that I can turn this unit test into an integration test. But I do not want to. Is there any fix for this? Do I have to use another packages?
My test suit is Jest 22.3.0.
After some search, I found the appropriate way to unit test the branch. We really do not have to use the mock-fs module. We just have to mock pathExists method of fs-extra module to return one time the value false and one time the value true. Bellow, I post a working version of my spec file:
import mainFunction from '../main'
require('fs-extra').pathExists = jest.fn().mockReturnValueOnce(false).mockReturnValueOnce(true)
describe('test main function', () => {
beforeEach(() => {
jest.clearAllMocks()
})
test('expect to not throw', async () => {
await expect(mainFunction('/dummy/path/does/not/matter')).resolves
})
test('expect to throw', async () => {
await expect(mainFunction('/dummy/path/does/not/matter')).rejects.toBeInstanceOf(Error)
})
})

Mocking node dependency for Typescript with Mocha / Sinon

I have a class which controls an audio receiver using an external library marantz-avr:
let AVReceiver = require('marantz-avr');
class MarantzPlugin {
hardwareInstance;
constructor(pluginConfiguration) {
const receiver = new AVReceiver(pluginConfiguration.settings.ip);
this.hardwareInstance = receiver;
}
turnPowerOn() {
this.hardwareInstance.setPowerState('on').then(res => res, error => console.error(error));
}
}
export default MarantzPlugin;
I want to unit test this class. In order to do so I have to mock the marantz-avr library since this library only works when an actual receiver is found on the provided ip address.
In the test below I mock the marantz-avr, however the MarantzPlugin still uses the original AVReceiver instead of the mocked one.
import { suite, test, slow, timeout } from 'mocha-typescript';
import * as mocha from 'mocha';
import * as assert from 'assert';
import * as sinon from 'sinon';
import * as should from 'should';
import MarantzPlugin from './';
let AVReceiver = require('marantz-avr');
#suite
class MarantzPluginTest {
public create() {
before(() => {
sinon.stub(AVReceiver.prototype, 'AVReceiver').callsFake(() => {
return {
setPowerState: () => {
return true;
}
}
});
});
let marantz = new MarantzPlugin({
id: 'MARANTZ',
settings: {
ip: '192.168.178.2',
}
});
marantz.setPowerState('on');
}
}
I've looked into http://sinonjs.org/how-to/link-seams-commonjs/ but when implemented it gave me Error: Cannot find module 'marantz-avr'
Does anyone see what I am missing here, or perhaps have a better way to unit test these kind of classes?

jest manual mock imports are not working

For some reason Jest is not picking up my manual mock implementation. The structure is the following. Am I missing something here? It is for sure hoisting the .mock call, I've tested it with the mock call above imports and still no dice.
Appreciate any help.
--
// "#/store/modules/__tests__/data"
import {fetchData} from "#/store/modules/data"
import {fetchByids} from "#/api/fetch";
jest.mock("#/api/fetch");
...
[ fetchByIds is considered the original still ? ]
--
// "#/store/modules/data"
import {fetchByIds} from "#/api/fetch";
...
export const fetchData = (context, payload) => {
fetchByIds(payload.id);
}
--
// "#/api/fetch"
export const fetchByIds = (id) => fetch(`url${id}`).then(response=>response.json())
--
// "#/api/__mocks__/fetch"
export const fetchByIds = jest.fn().mockImplementation(id => Promise.resolve({id}))
--
What's interesting is if I add the following to "#/store/modules/__tests__/data" it picks up the mock implementation. So maybe it's an issue with name mapping?
jest.mock("#/api/fetch", () => ({
fetchByIds: jest.fn().mockImplementation(id => Promise.resolve({id}))
}));

Resources