How do you unit test (with jest and enzyme) a functional component that uses the useReactiveVar hook?
Should I mock the useReactiveVar hook with jest?
you can mock whole apollo client in each test to give different responses. I mean:
jest.mock('#apollo/client', () => ({
makeVar: () => {
return {};
},
useReactiveVar: () => {
return { account: { name: 'Test' } };
},
}));
But this is helpful only when testing component that use only one reactiveVar at all.
Related
I am using the native NodeJs Testing Framework (imported from node:test).
When using the test syntax, I have access to the TestContext as test(...) callback argument :
test('top level test', async (t) => {
t.afterEach((t) => t.diagnostic(`finished running ${t.name}`));
await t.test(
'This is a subtest',
(t) => {
assert.ok('some relevant assertion here');
}
);
});
But how can I call diagnostic(...) when using the describe syntax ?
The example in the official documentation is not that helpful/incomplete (https://nodejs.org/api/test.html#beforeeach-fn-options) :
describe('tests', async () => {
beforeEach(() => t.diagnostic('about to run a test'));
it('is a subtest', () => {
assert.ok('some relevant assertion here');
});
});
since there is no TestContext argument given ...
Any help are appreciated :-)
I'm trying to mock the return value of messages.create() method from twilio-node library.
Since the create method resides inside the interface called messages, i can't directly mock the return value of create method.
My Unit test:
import {
createStubInstance,
StubbedInstanceWithSinonAccessor,
} from '#loopback/testlab';
import sinon from 'sinon';
import {Twilio} from '../../../../clients/whatsapp-sms-clients/twilio.whatsapp-sms-clients';
import twilio from 'twilio';
describe('Twilio client (UnitTest)', () => {
let twilioMock: StubbedInstanceWithSinonAccessor<twilio.Twilio>;
let logger: StubbedInstanceWithSinonAccessor<LoggingService>;
let twilioClient: Twilio;
beforeEach(() => {
twilioMock = createStubInstance(twilio.Twilio);
logger = createStubInstance(LoggingService);
twilioClient = new Twilio(twilioMock, logger);
});
it('should create the message', async () => {
twilioMock.stubs.messages.create.resolves({
// mocked value
});
});
});
Thanks in advance.
Twilio developer evangelist here.
I've not worked with testlab/sinon like this before, but I think I have an idea of what you need to do, if not the right syntax.
You'd need to stub the response to twilioMock.messages to return an object that has a create property that is a stubbed function that resolves to the result you want. Something like this might work, or at least set you on the right track:
it('should create the message', async () => {
// Create stub for response to create method:
const createStub = sinon.stub().resolves({
// mocked value
});
// Stub the value "messages" to return an object that has a create property with the above stub:
twilioMock.stubs.messages.value({
create: createStub
});
// Rest of the test script
});
Edit
OK, using value above didn't work. I tried again. This version strips out your custom Twilio wrapper from the example and just calls things directly on the Twilio client stub itself. Hopefully you can use this as inspiration to work it into your tests.
What I realised is that twilioClient.messages is a getter and is dynamically defined. So, I directly stubbed the result on the stub client.
import {
createStubInstance,
StubbedInstanceWithSinonAccessor,
} from "#loopback/testlab";
import sinon from "sinon";
import { Twilio } from "twilio";
describe("Twilio client (UnitTest)", () => {
let twilioMock: StubbedInstanceWithSinonAccessor<Twilio>;
beforeEach(() => {
twilioMock = createStubInstance(Twilio);
});
it("should create the message", async () => {
const createStub = sinon.stub().resolves({
sid: "SM1234567",
});
sinon.stub(twilioMock, "messages").get(() => ({
create: createStub,
}));
const message = await twilioMock.messages.create({
to: "blah",
from: "blah",
body: "hello",
});
expect(message.sid).toEqual("SM1234567");
});
});
The above test passes for me in my setup.
I am testing a class DogService.ts. But DogService has a private dependency called CatService that gets new'ed up in the constructor. I can successfully mock the dependency using:
jest.mock('../CatService', () => {
return {
CatService: jest.fn().mockImplementation(() => {
return {
someFunction: (async (someArg) => {
return await someAsyncMethod();
})
}
})
}
});
And it gets injected fine in the test:
describe("", () => {
it("", async () => {
const dogService = new DogService();
...
});
});
There's no import for the CatService in my jest test file. The mock I guess is the import. However, I want to swap out the implementation of CatService in different tests. I have tried jest.resetModules() and a bunch of other things. Further, the above only seems to work when the mock is outside any tests. If I move it into the describe() function the real CatService is used.
Any ideas how to do this? Most tutorials are for mocking classes or functions you directly use in the jest test, but obviously I am mocking a dependency.
I have an application that includes a Node script that runs Lighthouse v3 programatically and headlessly (Lighthouse documentation) to test the application's performance.
I wrote some Jest tests for this which pass locally. The tests are not testing Lighthouse itself, but rather how the Node script deals with with the results data returned from Lighthouse. Therefore, the Lighthouse dependency must be mocked.
In the course of doing my testing I have discovered that chrome-launcher, which is invoked by Lighthouse, launches when I'm doing my Jest test. This means I am not mocking this dependency correctly. I thought it was enough to mock Lighthouse and have done so but I am confused about how I can mock the 'thennable' chrome launcher function function.
The launchChromeAndRunLighthouse function below is from the Lighthouse Node documentation.
lighthouse.js
const lighthouse = require('lighthouse');
const chromeLauncher = require('chrome-launcher');
function launchChromeAndRunLighthouse(url, opts, lConfig = null) {
return chromeLauncher
.launch({ chromeFlags: opts.chromeFlags })
.then(chrome => {
const options = { ...opts, port: chrome.port };
return lighthouse(url, options, lConfig).then(results =>
chrome.kill().then(() => results.lhr),
);
});
}
function myFunc(config) {
launchChromeAndRunLighthouse(myAppUrl, config);
// manipulates data returned from launchChromeAndRunLighthouse
return true;
}
module.exports = myFunc;
lighthouse.test.js
import myFunc from './lighthouse';
jest.mock('lighthouse', () =>
jest.fn().mockResolvedValue({
lhr: {
categories: {
performance: {
id: 'performance',
score: 1,
}
},
},
}),
);
// chromeLauncher actually gets invoked by this
describe('myfunc', () => {
it('tests myfunc', async () => {
const result = await myFunc(config);
expect(result).toEqual(true);
});
});
New to Jest and confused at how I can mock chromeLauncher so it's prevented from launching. Thanks
You don't need to mock chromeLauncher, leave it work like it does. Just mocking lighthouse is enough to make tests run and pass. It works as expected in my projects.
I am writing tests for a REST client library which has to "login" against the service using the OAuth exchange. In order to prevent logging in for every endpoint I am going to test I'd like to write some sort of "test setup" but I am not sure how I am supposed to do this.
My test project structure:
test
endpoint-category1.spec.ts
endpoint-category2.spec.ts
If I had only one "endpoint category" I had something like this:
describe('Endpoint category 1', () => {
let api: Client = null;
before(() => {
api = new Client(credentials);
});
it('should successfully login using the test credentials', async () => {
await api.login();
});
it('should return xyz\'s profile', async () => {
const r: Lookup = await api.lookup('xyz');
expect(r).to.be.an('object');
});
});
My Question:
Since the login() method is the first test there, it would work and the client instance is available for all the following tests as well. However, how can I do some sort of setup where I make the "logged in api instance" available to my other test files?
Common code should be moved to beforeEach:
beforeEach(async () => {
await api.login();
});
At this point should successfully login using the test credentials doesn't make much sense because it doesn't assert anything.
describe('Endpoint category 1', () => {
let api: Client = null;
beforeEach(() => {
api = new Client(credentials);
});
afterEach(() => {
// You should make every single test to be ran in a clean environment.
// So do some jobs here, to clean all data created by previous tests.
});
it('should successfully login using the test credentials', async () => {
const ret = await api.login();
// Do some assert for `ret`.
});
context('the other tests', () => {
beforeEach(() => api.login());
it('should return xyz\'s profile', async () => {
const r: Lookup = await api.lookup('xyz');
expect(r).to.be.an('object');
});
});
});
Have you had a look at https://mochajs.org/#asynchronous-code ?
You can put in a done-parameter in your test functions and you will get a callback with this you have to call.
done() or done(error/exception)
This done would be also available in before and after.
When calling done() mocha knows your async-code has finished.
Ah. And if you want to test for login, you shouldn't provide this connection to other tests, because there is no guarantee of test order in default configuration.
Just test for login and logout afterwards.
If you need more tests with "login-session", describe a new one with befores.