I'm currenty trying to write unit tests for my Nestjs Backend. Therefore i use the integrated Jest Framework. Now i have problems with mocking static exports without a certain class.
This is the function i want to test:
export const createGetLatestAssetsMetadataQuery = ({
assetsCatalogueVersion,
sorting,
}: AFilter): RequestParams.Search<any> => {
const match = createCatalogueVersionMatchFactory(VERSION_TIME_STAMP_FIELD)(
assetsCatalogueVersion
);
...
return ...
This is the function which is called to set the match constant. This method is inside an Util-File but doesn't belong to an Class.
export const createCatalogueVersionMatchFactory = (basePath: string) => (
filter?: string
): { [key: string]: { query: any } } => {
const result: { [key: string]: any } = {};
if (filter) {
const date = parseISO(normalizeDTStringTZ(filter));
result[basePath] = { query: date };
}
return result;
};
The Goal is to mock the Return Value of this function.
For functions inside a service i can use
jest.spyOn(service, "functionname").mockResolvedValue(returnValue);
How can i do it for static functions without a service
Related
using nestjs framework and with a repository class that uses mongoose to do the CRUD operations we have a simple users.repository.ts file like this:
#Injectable()
export class UserRepository {
constructor(#InjectModel(User.name) private userModel: Model<UserDocument>) {}
async create(createUserInput: CreateUserInput) {
const createdUser = new this.userModel(createUserInput);
return await createdUser.save();
}
}
async findById(_id: MongooseSchema.Types.ObjectId) {
return await this.userModel.findById(_id).exec();
}
and it works normally when the server is up.
consider this users.repository.spec file :
import { Test, TestingModule } from '#nestjs/testing';
import { getModelToken } from '#nestjs/mongoose';
import { Model } from 'mongoose';
// User is my class and UserDocument is my typescript type
// ie. export type UserDocument = User & Document; <-- Mongoose Type
import { User, UserDocument } from '../domain/user.model';
import { UserRepository } from './users.repository';
//import graphqlScalars from 'graphql-scalar-types';
describe('UsersRepository', () => {
let mockUserModel: Model<UserDocument>;
let mockRepository: UserRepository;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: getModelToken(User.name),
useValue: Model, // <-- Use the Model Class from Mongoose
},
UserRepository,
//graphqlScalars,
],
}).compile();
// Make sure to use the correct Document Type for the 'module.get' func
mockUserModel = module.get<Model<UserDocument>>(getModelToken(User.name));
mockRepository = module.get<UserRepository>(UserRepository);
});
it('should be defined', () => {
expect(mockRepository).toBeDefined();
});
it('should return a user doc', async () => {
// arrange
const user = new User();
const userId = user._id;
const spy = jest
.spyOn(mockUserModel, 'findById') // <- spy
.mockResolvedValue(user as UserDocument); // <- set resolved value
// act
await mockRepository.findById(userId);
// assert
expect(spy).toBeCalled();
});
});
so my question:
for the should return a user doc test i get TypeError: metatype is not a constructor when and i guess
.mockResolvedValue(user as UserDocument);
should be fixed.
Note:graphql is used the query to the API and i have no idea that if the scalars should be provieded or not, if i uncomment the scalar, the expect(mockRepository).toBeDefined(); test would not pass any more
so any idea to fix the test would be apreciated.
to handle a chained .exec we should define it via mockReturnThis():
static findById = jest.fn().mockReturnThis();
I needed the constructor to be called via new so i preferd to define a mock class in this way:
class UserModelMock {
constructor(private data) {}
new = jest.fn().mockResolvedValue(this.data);
save = jest.fn().mockResolvedValue(this.data);
static find = jest.fn().mockResolvedValue(mockUser());
static create = jest.fn().mockResolvedValue(mockUser());
static remove = jest.fn().mockResolvedValueOnce(true);
static exists = jest.fn().mockResolvedValue(false);
static findOne = jest.fn().mockResolvedValue(mockUser());
static findByIdAndUpdate = jest.fn().mockResolvedValue(mockUser());
static findByIdAndDelete = jest.fn().mockReturnThis();
static exec = jest.fn();
static deleteOne = jest.fn().mockResolvedValue(true);
static findById = jest.fn().mockReturnThis();
}
I've set up CodeceptJS for a project and use it to test various end-to-end scenarios.
Now I want to extend the tests-suite to also run unit-tests to verify functionality of custom JS functions.
For example: I have a global object App that has a version attribute. As a first test, I want to confirm that App.version is present and has a value.
My first attempt is a test.js file with the following code:
Feature('Unit Tests');
Scenario('Test App presence', ({ I }) => {
I.amOnPage('/');
I.executeScript(function() {return App.version})
.then(function(value) { I.say(value) } );
});
Problems with this code
The major issue: How can I assert that the App.version is present?
My script can display the value but does not fail if it's missing
My code is very complex for such a simple test.
I'm sure there's a cleaner/faster way to perform that test, right?
Here is a solution that works for me:
Read data from the browser:
I created a custom helper via npx codecept gh and named it BrowserAccess.
The helper function getBrowserData uses this.helpers['Puppeteer'].page.evaluate() to run and return custom code from the browser scope. Documentation for .evaluate()
Custom assertions:
Install the codeceptjs-assert package, e.g. npm i codeceptjs-assert
Add the AssertWrapper-helper to the codecept-config file. This enables checks like I.assert(a, b)
Full Code
codecept.conf.js
exports.config = {
helpers: {
AssertWrapper: {
require: "codeceptjs-assert"
},
BrowserAccess: {
require: './browseraccess_helper.js'
},
...
},
...
}
browseraccess_helper.js
const Helper = require('#codeceptjs/helper');
class BrowserAccess extends Helper {
async getBrowserData(symbolName) {
const currentPage = this.helpers['Puppeteer'].page;
let res;
try {
res = await currentPage.evaluate((evalVar) => {
let res;
try {
res = eval(evalVar);
} catch (e) {
}
return Promise.resolve(res);
}, symbolName);
} catch (err) {
res = null;
}
return res;
}
}
jsapp_test.js (the test is now async)
Feature('Unit Tests');
Scenario('Test App presence', async ({ I }) => {
I.amOnPage('/');
const version = await I.getBrowserData('App.version');
I.assertOk(version);
});
We have a function like this:
export function* postPermitApplicationRequest() {
try {
const uris = yield select(state => getUris(state));
.... (more yields hereafter)
We test this function with Jest and Chai as follows:
...
const action = { type: Action.POST_PERMIT_APPLICATION };
const generator = postPermitApplicationRequest(action);
it('must select uris from state', () => {
const nextCall = generator.next().value;
const uris = select(state => getUris(state));
expect(nextCall).to.deep.equal(uris);
});
However the expect fails:
AssertionError: expected { Object (##redux-saga/IO, SELECT) } to deeply equal { Object (##redux-saga/IO, SELECT) }
at Assertion.assertEqual (node_modules/chai/lib/chai/core/assertions.js:485:19)
at Assertion.ctx.(anonymous function) [as equal] (node_modules/chai/lib/chai/utils/addMethod.js:41:25)
at Object.<anonymous> (src/app/pa/PermitApplicationServiceSagas.test.js:20:43)
at process._tickCallback (internal/process/next_tick.js:109:7)
The two objects both look like:
{ '##redux-saga/IO': true,
SELECT: { selector: [Function], args: [] } }
However the selector functions are different. The one that is the outcome of generator.next() contains code coverage skip hints:
function (state) {/* istanbul ignore next */cov_zrpq42gyn.f[12]++;cov_zrpq42gyn.s[19]++;return (/* istanbul ignore next */(0, _Selectors.getUris)(state));}
while the original function doesn't:
function (state) {return (0, _Selectors.getUris)(state);}
It looks like generator.next() adds these hints and the assertion fails
What do we wrong here?
We use redux-saga 0.14.8
The tests fail because in your saga and your test you create a new functions every time you execute the code. These both functions will be compared but are not the same instance.
You can simply use select(getUris) in your saga and your test because both will reference to the same function.
Your saga:
export function* postPermitApplicationRequest() {
try {
const uris = yield select(getUris);
.... (more yields hereafter)
Your test:
...
const action = { type: Action.POST_PERMIT_APPLICATION };
const generator = postPermitApplicationRequest(action);
it('must select uris from state', () => {
const nextCall = generator.next().value;
const uris = select(getUris);
expect(nextCall).to.deep.equal(uris);
});
I would like to stub a private variable inside a class
class IPC {
private publisher: redis.RedisClient;
constructor() {
this.publisher = redis.createClient();
}
publish(text: string) {
const msg = {
text: text
};
this.publisher.publish('hello', JSON.stringify(msg));
}
}
How can I stub the private variable publisher , inside this class?
so I could test the code as shown below
it('should return text object', () => {
const ipc = sinon.createStubInstance(IPC);
ipc.publish('world!');
// this will throw error, because ipc.publisher is undefined
assert.deepStrictEqual({
text: 'world!'
}, ipc.publisher.getCall(0).args[0])
})
You can use type assertion in order to get access to the private variable. Like:
(ipc as any).publisher
There is no way to stub a private variable, and this is not the right way to do it, you could see the discussion below with Christian Johansen
https://groups.google.com/forum/#!topic/sinonjs/ixtXspcamg8
The best approach, is to inject any dependency into the constructor, once we refactor the code, we could easily stub the dependency with our required behaviour
class IPC {
constructor(private publisher: redis.RedisClient) {
}
publish(text: string) {
const msg = {
text: text
};
this.publisher.publish('hello', JSON.stringify(msg));
}
}
it('should return text object', () => {
sinon.stub(redis, 'createClient')
.returns({
publish: sinon.spy()
});
const publisherStub = redis.createClient();
const ipc = new IPC(publisherStub)
ipc.publish('world!');
// this is working fine now
assert.deepStrictEqual({
text: 'world!'
}, publisherStub.publish.getCall(0).args[0])
sinon.restore(redis.createClient)
})
I have an angular service that is used as a factory function to instatiate many object instances of the type Engine like this:
angular.module('parts.engine', []).factory('Engine', function() {
var Engine = function( settings ) {
this.hp = settings.engine.hp;
this.miles = 0;
};
Engine.prototype.setMiles = function( miles ) { this.miles = miles; }
return Engine;
});
Say I have another angular service, that is also used to create instances of an object like this:
angular.module('car', ['parts.engine']).factory('carCreator', function( Engine ) {
var carCreator = function( settings ) {
var engine = new Engine( settings );
engine.setMiles( settings.engine.miles )
return {
brand: settings.brand;
engine: engine;
}
};
return carCreator;
});
So I now instatiate a new instance of a car object like this:
angular.module('carApp', ['car']).controller('AppCtrl', function( carCreator ) {
var settings = {
brand: 'Ford',
engine: {
hp: 125,
miles: 12000
}
};
var newCar = carCreator(settings);
});
Does anyone have an idea how to test the initialization logic:
var engine = new Engine( settings );
engine.setMiles( settings.engine.miles )
in the carCreator factory? I know I can instantiate an object with the carCreator class and check, if the returned objects engine.miles property is set to the correct value. But I have cases, where checking for this will not be as easy, because the initalization logic and the values returned are much more complex. What I would like to do is test the businesslogic of setMiles in the Engine class, and just setup a SpyOn on Engine and engine.setMiles when testing the carCreator class, but how do I do that, when engine is created in the closure?
You can use $provide to provide a mock version of the Engine service to carCreator. You'll then be able to spy on it and assert that the correct steps have been taken.
One of the trickier bits of this is that you're testing a constructor function, which jasmine mocks doesn't play so nicely with, you need to return a mockInstance and spy on that instance.
var mockEngine, mockEngineInstance, carCreatorService;
beforeEach(function () {
module('car', function ($provide) {
// Define a mock instance
mockEngineInstance = {
setMiles: function () {}
};
// Mock the engine service to give a mocked instance
mockEngine = function () {
return mockEngineInstance;
};
$provide.value('Engine', mockEngine);
});
inject(function (carCreator) {
carCreatorService = carCreator;
});
});
it('should test that miles are set when a car is created',
function () {
// Arrange.
spyOn(mockEngineInstance, 'setMiles');
// Act.
carCreatorService({
engine: {
miles: 100
}
});
// Assert.
expect(mockEngineInstance.setMiles).toHaveBeenCalledWith(100);
}
);