Mocking individual methods of external module differently each time in jest - jestjs

I want to test my service methods which is dependent on the external module azure-devops-extension-api.
Currently I have created __mocks__\azure-devops-extension-sdk.ts and provide the custom implementation which needs to be mocked. It works fine. But there are some issues with unit testing as asked in Methods called on jest mocked response fails with error.
But I don't have to feasibility to change values for each of the unit test. Values that needs to be provided in mock will vary based on the unit test case.
for example, below is my service method which uses the getService() method from SDK of azure-devops-extension-api module.
import * as SDK from 'azure-devops-extension-sdk';
// more stmts
const projectService = await SDK.getService<IProjectPageService>(CommonServiceIds.ProjectPageService);
const project = await projectService.getProject();
For first unit test, I want to mock the getProject() method to return a valid value and for the second unit test, I want to be a null.
Most common solution is the below approach, but as requested, I want the flexity to mock differently for each unit test.
jest.mock('azure-devops-extension-sdk', () => {
return { getService: (client: any) => { id: 1, name: 'project' } };
});

Related

Jest mocking private members

In a Node/Express server, we use a repository that needs to be unit-tested using Jest.
//Private things
let products;
function loadProducts() {
if (!products)
products = fetchProductsFromSomeDbOrServiceOrWhatever()
}
function saveProducts() {
persistPrivateProductsToADbOrServiceOrWhatever()
}
// Exported/public things
export function read() {
loadProducts();
return products;
}
export function add(product) {
loadProducts();
products.push(product);
saveProducts();
}
We want to unit test like this:
import { read, add } from './productRepo';
it('can read products', () => {
expect(read().length).toBe(5);
});
it('can add a product', () => {
const oldNum = read().length;
add({id:0, name:'test prod', moreProps});
expect(read().length).toBe(oldNum+1)
});
You get the idea. It's not a class so we can't mess with the prototype.
Problem: How do I mock the private products and/or loadProducts and/or saveProducts so that it isn't reading from the actual data source?
Presumably these private functions call out to other pieces of functionality you've written yourself or imported from libraries.
function loadProducts() {
if (!products)
products = fetchProductsFromSomeDbOrServiceOrWhatever()
}
function saveProducts() {
persistPrivateProductsToADbOrServiceOrWhatever()
}
Let's take fetchProductsFromSomeDbOrServiceOrWhatever as the example. One basic architectural consideration to make the code properly encapsulated and testable is to put this functionality in a separate module. So I would expect an import at the head of the file:
import fetchProductsFromSomeDbOrServiceOrWhatever from './fetchProductsFromSomeDbOrServiceOrWhatever'
So in this case just mock it in your test file:
jest.mock('./fetchProductsFromSomeDbOrServiceOrWhatever');
If the functionality is not extracted into a separate module this makes your code less testable; this on its own is a good reason to refactor.
Note: the other replies on this thread are correct when they say that private functions of classes should not be tested, but I think that is a slightly different issue from the one you are asking.
First, start initializing products to an empty array, else tests are doomed to fail because of the null value. Also change the null check
Then parametrize your loader and saver functions so your functions can be testable. Last write tests for you loader and saver functions outside of this repo function.
// assummed imports
fetchProductsFromSomeDbOrServiceOrWhatever=()=>{}
persistPrivateProductsToADbOrServiceOrWhatever=()=>{}
//Private things
let products=[];
function loadProducts(loader) {
loader=loader || fetchProductsFromSomeDbOrServiceOrWhatever
if (products.length==0)
products = loader()
}
function saveProducts(saver) {
saver=saver || persistPrivateProductsToADbOrServiceOrWhatever
saver()
}
// Exported/public things
export function read(loader) {
loadProducts(loader);
return products;
}
export function add(product,loader,saver) {
loadProducts(loader);
products.push(product);
saveProducts(saver);
}
both exported functions can now use fetch/persist functions either by importing or as arguments.
Now the remaining is the mocking loader and saver function. saver function does not change anything so it can be null or empty. but if you want to check if it is called inside, then you need to mock it.
import {jest} from '#jest/globals'
import { read, add } from './productRepo';
it('can read products', () => {
loader=jest.fn().mockReturnValue([{id:7},{id:42}])
expect(read(loader).length).toBe(2);
expect(loader).toBeCalledTimes(1)
});
it('can add a product', () => {
loader=jest.fn().mockReturnValue([{id:7},{id:42}])
saver=jest.fn()
const oldNum = read(loader).length;
add({id:0, name:'test prod'},loader,saver);
expect(read(loader).length).toBe(oldNum+1)
expect(loader).toBeCalledTimes(0)
expect(saver).toBeCalledTimes(1)
});
There is a "gotcha" here. Since productRepo is imported once, loader is called in the first test but will not be called again in the second test since the first has already changed the products. Thus subsequent tests must take this into account when using non-class packages.
you must not get access to private properties or methodes anyway.
instead you can provide setter and getter for your properties.
for methodes I believe you can break it into some private parts and some public parts. private parts for your actual data source and public parts that can be used in test either.
I suggest implementing an initialize method on productRepo.js.
export function init(data) {
products = data
}
Then, you can init products with mocked data.
Also, if you can't change the file, you could use the rewire library, which lets you access non-exported functions and variables.

How to mock a 'request' node module for testing in jest

I am new to writing test cases in jest and i wanted to test 'mark' function and want to mock 'request' node module. let's say this file's name is app.js and test file will be app.test.js
Can someone tell how to write its test case?
const request = require("request")
var test = {
mark: function(data,cb){
data.url = "localhost"
request(data, function(err,response,body){
if(!response){
err.response = false
}
cb(err,body)
})
}
}
module.exports = test;
If I understand your question correctly, you are asking two questions:
How to write the test and mock the request
What test cases you should write
Regarding the mock, I recommend using nock which is an npm module for mocking network requests.
To the second question, I believe that you should map the logic in the function and create the test cases from there. Think about the function, look at every if else/calculation/loop and it’s possible outcomes and create test cases from there.
The mark function doesn’t have a lot of logic, it’s send a request, updates the err variable according to the response and calling the callback.
The only outcome we can test to to see that if the request works, the cb is called correctly without modifications. And if the request returns empty, change the err var and call the callback.
To do so we need to mock the request and return a response to match the test case and validate that the cb was called correctly (can be done with a mock).
Test case example:
The test case may be a bit abstract since I don’t have the real use case of the function but it shows the point
it("Should mark response false when it does not exist", () => {
const DATA = {} // your data object
const callback = jest.fn();
nock('localhost')
.get('/example')
.reply(500) // Exaple of mocking the resonse. Custom it to be more specific depending on mark.
test.mark(DATA, callback);
// Verify that the function was called.
expect(callback).toHaveBeenCalled();
// Verify that `err.response` was false.
expect(callback.mock.calls[0][0].response).toBe(false)
})
You can read more about mocking and verifying parameters here.

Get Jest test name within beforeEach() and afterEach()

I am running Jest and am trying to log the start and end timestamp for each of my tests. I am trying to stick my timestamp logging inside the beforeEach() and afterEach() blocks. How would I log the name of my Jest test within the beforeEach() and afterEach() block?
Also, is there a more global way of logging test name and timestamp before and after all the tests without using beforeEach() and afterEach()?
You can access the name of the current test in jest like this:
expect.getState().currentTestName
This method also works inside beforeEach / afterEach
The only downside is that it will also contain the name of your current describe section. (which may be fine depending on what you are trying to do.
Also it does not give you the timing information that you asked for.
The information on currently running test is unavailable in beforeEach. Similarly to Jasmine, suite object is available in Jest as this context in describe function, it's possible to patch spec definitions to expose needed data. A more trivial way would be to define custom wrapper function for global it that intercepts test name.
Custom reporter is a better way to do this. Reporter interface is self-documented, necessary data is available in testResult.
Performance measurements are already available:
module.exports = class TimeReporter {
onTestResult(test, testResult, aggregatedResult) {
for (let { title, duration } of testResult.testResults)
console.log(`test '${title}': ${duration} ms`);
}
}
Can be used like:
reporters: ['default', "<rootDir>/time-reporter.js"]
As it was noted, there are beforeAll and afterAll, they run once per describe test group.
You can set up a test environment and either log times directly or write the name and timing info into a global variable that is only available inside the tests in question:
./tests/testEnvironment.js
const NodeEnvironment = require('jest-environment-node');
class TestEnvironment extends NodeEnvironment {
constructor(config, context) {
super(config, context);
}
async setup() {
await super.setup();
}
async teardown() {
await super.teardown();
}
async handleTestEvent(event, state) {
if (event.name === 'test_start') {
// Log things when the test starts
} else if (event.name === 'test_done') {
console.log(event.test.name);
console.log(event.test.startedAt);
console.log(event.test.duration);
this.global.someVar = 'set up vars that are available as globals inside the tests';
}
}
}
module.exports = TestEnvironment;
For each test suite the following comment is needed to use this environment:
/**
* #jest-environment ./tests/testEnvironment
*/
Also see https://jestjs.io/docs/configuration#testenvironment-string

How to pass data from a test to a reporter in jest?

I'm using a custom jest reporter to populate data in testrail (a test case management software) and would like my jest tests to be the source of truth for all data being fed into the test case management software.
I've been struggling a bit to understand how I could pass additional data from the test to the reporter. I'm testing a GraphQL API, and would like the actual API payload to make its way, from the test to testrail, plus eventually additional metadata later on.
The only data elements I'm able to use are:
ancestorTitles: [Array],
duration: 52,
failureMessages: [],
fullName: 'Test suite - test case',
location: null,
numPassingAsserts: 0,
status: 'passed',
title: 'test case'
For example, a test case looks like this:
describe('My Test Suite', () => {
test('My test case', async done => {
const query = `
{
query {
documents {
totalCount
}
}
}`
const response = await graphQL(query, global.apiConfig)
const hits = response.data.documents.totalCount
expect(hits).toHaveLength(4)
done();
}
)
How could I have query passed down to the reporter?
#FrancoiG Until I find something better I did following.
I am using TestRail as well. So my each test name starts with case number from testRail.
import testCustomData from ('./config/testCustomData.json')
describe('My Test Suite', () => {
test('C111: My test case', async done => {
const customData = testCustomData.C111;
...
...
}
)
With this same code I can access test data from Jest TestRail Reporter as case id is present in test name. In my situation data was not dynamically generated so I can use this approach.
In your case if query id dynamically created then you can generate file on the fly where you will store it with testrail or any other unique identifier key. Here in my case was: C111.
Then you can access it from Jest Reporter.
It worked or me, but I hope there is some better solution, like extending testResult with custom data, etc.

are there issues with generating a new wrapper for each test in Enzyme?

I'm doing tests for my React project using Jest + Enzyme.
Currently I would generate a new wrapper for each test in a suite.
example:
it('should render a title', () => {
let wrapper = shallow(<Component />);
expect(wrapper.find('#title')).toHaveLength(1);
});
it('should call closeModal function when clicked', () => {
let wrapper = shallow(<Component />);
wrapper.instance().closeModal = jest.fn();
let targetFunction = wrapper.instance().closeModal;
expect(targetFunction).toHaveBeenCalled();
});
I would like to know whether this is the standard or should I be generating the wrapper in a beforeAll and referencing that one.
I'm interested in this for the potential improvement in speed time. Right now I have 190 tests and they are done in 21.38s.
The problem with beforeAll is that it will use the same instance in all of your test. If you now change the internal state or props of a component in one of you test this can influent the result of the other test.
Normally I would use beforeAll to test different parts of a component without having a generic test like 'renders correct' but multiple small ones like 'renders the title', 'renders the body' and so on, were every test tests a single part of the component, as this will make it easier to find the place where something went wrong if the test fails.

Resources