I have following classes and files
//A.ts
export abstract class A{
protected abstract method1(arg){
}
}
// C.ts
export interface C{
public methodC1(){
};
}
//CImpl.ts
export class CImpl implements C{
public methodC1(){
// do something
};
}
// B.ts
import appConfig from 'config';
export class B extends A{
private _enable : boolean appConfig.get('IsEnabledC');
private init(argC:C | undefined)
{
if(this._enable){
argC = new CImpl();
}
argC.methodC1();
return argC;
}
private func(argC){
let resource = getResource();
switch(resource.status){
case 1:{
throw(some exception);
}
case 2:{
throw(another exception);
}
default:{
// do something
}
}
private cleanup(){
//do something;
}
protected async method1(arg){
let argC: C | undefined;
try{
argC = init(argC);
await this.func(argC);
}
catch(exception){
this.cleanup();
}
}
}
// B.spec.ts
describe("B test", () => {
it("check if cleanup is called on resource status 1", () => {
const varB = new B();
const spied = jest.spyOn(B.prototype as any,"init").mockReturnValue(new CImpl(argC));
const spied2 = jest.spyOn(B.prototype as any,"cleanup").mockImplementation(()=>{console.log("cleanup got called")});
await varB["method1"](arg);
expect(spied).toBeCalledTimes(1);
expect(spied2).toBeCalledTimes(1);
});
it("check if cleanup is called on resource status 2", () => {
const varB = new B();
const spied = jest.spyOn(B.prototype as any,"init").mockReturnValue(new CImpl(argC));
const spied2 = jest.spyOn(B.prototype as any,"cleanup").mockImplementation(()=>{console.log("cleanup got called")});
await varB["method1"](arg);
expect(spied).toBeCalledTimes(1);
expect(spied2).toBeCalledTimes(1);
});
})
I want to test two status viz. 1 and 2 for resource inside func method. I have also mocked the getResource() method and setting status 1 and 2 in test respectively. I want that cleanup method gets called only once in each test.
But eventually in second test both spied and spied2 are getting called 2 times.
When I am running each test separately then they are working fine, but when they are run together first test is getting passed and second test is getting failed in expect assertion with message jest.fn() is called 2 times.
How can I reset state after/before each test?
you can use these methods: beforeAll, afterAll, beforeEach, afterEach.
https://jestjs.io/docs/setup-teardown
for clearing mocks you can use .mockClear()
https://jestjs.io/docs/mock-function-api#mockfnmockclear
upd:
You can use it only on mocked functions. It can be called anywhere in a test, but you'll probably need it inside afterAll. It will look like this:
import { yourMockedFunction } from '...'
afterAll(() => {
yourMockedFunction.clearMock();
});
// your tests here
Related
In Sinon I can do the following:
var myObj = {
prop: 'foo'
};
sinon.stub(myObj, 'prop').get(function getterFn() {
return 'bar';
});
myObj.prop; // 'bar'
But how can I do the same with Jest?
I can't just overwrite the function with something like jest.fn(), because it won't replace the getter
"can't set the value of get"
For anyone else stumbling across this answer, Jest 22.1.0 introduced the ability to spy on getter and setter methods.
Edit: like in scieslak's answer below, because you can spy on getter and setter methods, you can use Jest mocks with them, just like with any other function:
class MyClass {
get something() {
return 'foo'
}
}
jest.spyOn(MyClass.prototype, 'something', 'get').mockReturnValue('bar')
const something = new MyClass().something
expect(something).toEqual('bar')
You could use Object.defineProperty
Object.defineProperty(myObj, 'prop', {
get: jest.fn(() => 'bar'),
set: jest.fn()
});
If you care about spying only, go for #Franey 's answer. However if you actually need to stub a value for the getter, this is how you can do it:
class Awesomeness {
get isAwesome() {
return true
}
}
describe('Awesomeness', () => {
it('is not always awesome', () => {
const awesomeness = new Awesomeness
jest.spyOn(awesomeness, 'isAwesome', 'get').mockReturnValue(false)
expect(awesomeness.isAwesome).toEqual(false)
})
})
In my unity test case, as it is expected to mock external dependencies, following the thomaux's answer, i had to use a real instance instead of a mocked one in beforeEach function, see the snippet bellow.
//declaration of MyExternalConfigService with getter
#Injectable()
export class MyExternalConfigService {
constructor(private readonly configService: ConfigService) {}
get myProp():string {
return this.configService.get<string>('context.myProp')
}
}
//beforeEach unit test configuration
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
MyServiceToBeTested,
{
provide: MyExternalConfigService,
useValue: new MyExternalConfigService(new ConfigService())
}
]
}).compile()
service = module.get<MyServiceToBeTested>(
MyServiceToBeTested
)
configService = module.get<MyExternalConfigService>MyExternalConfigService)
})
//mocking a value to your test case
it('my test case', ()=>{
jest
.spyOn(configService, 'myProp', 'get')
.mockImplementationOnce(() => 'mockedValue')
...
)
In the 3rd party lib, There's the following:
export interface Event extends Log {
args?: Result;
}
export interface TypedEvent<EventArgs extends Result> extends Event {
args: EventArgs;
}
export type InstallationPreparedEvent = TypedEvent<
[
string,
string
] & {
sender: string;
permissions : string;
}
>;
Then, in my code, I got this.
function prepareInstall(...): Promise<InstallationPreparedEvent> {
const event = ...;
return event;
}
Then, I got these choices, but I like none of them. The reason is, I simplified it, but it actualy contains 8 args, so code becomes ugly.
// choice 1.
function run() {
const event = await prepareInstall();
string sender = event.args.sender;
}
// choice 2
function run() {
const { event : {sender } } = await prepareInstall();
}
// choice 3
function run() {
const { sender } = (await prepareInstall()).args;
}
What I want to achieve is from prepareInstall, I want to return event.args directly so I'd use it like this:
const { sender } = await prepareInstall();
But returning event.args doesn't solve it as typescript in the run still requires me to do: event.args.sender. The important thing is I need returned type in prepareInstall to be InstallationPreparedEvent since in run, I want typescript to tell me what values exist(sender, permissions).
Hope I made sense.
If I understand correctly, you want to return only the arg prop from InstallationPreparedEvent, but with correct typing (right?).
You can specify a prop's type through bracket notation:
function prepareInstall(...): Promise<InstallationPreparedEvent["args"]> {
const event = await ...;
return event.args;
}
With this, you will get type hinting:
function run() {
const eventArgs = await prepareInstall();
const {sender} = eventArgs; // no complaints
const foo = eventArgs.foo; //Error: Property 'foo' does not exist on type '[string, string] & { sender: string; permissions: string; }'
}
I'm trying to define a simple assertion package in TypeScript so that assertions can be turned off during efficiency testing (and production eventually). Here's my attempt for a simple module:
let assertionsEnabled = true;
export function withAssertions<T>(val : boolean, body : () => T) : T {
const savedEnabled = assertionsEnabled;
try {
assertionsEnabled = val;
return body();
} finally {
assertionsEnabled = savedEnabled;
}
}
export function assert(condition : () => boolean, message ?: string) {
if (assertionsEnabled) {
if (!condition()) {
throw new Error("assertion failed" + (message ?? ""));
}
}
}
The problem is that both the Jest test and the class being tested each import this module and they seem to end up with two different assertionsEnabled variables. My test is encoded with
describe('building', () => {
withAssertions(false, () => {
...
});
});
and in the ADT, I have a bunch of assertions, but even though assertions are turned off in the Jest test, they are still on in the ADT. I thought modules were supposed to have a single instance. But obviously I'm doing something wrong.
I'm betting you have asynchronous stuff going on in your tests or stuff you instantiate as part of exporting from the module and it's happening before the jest stuff starts being executed.
Also, you might instead want to do a describeWithAssertion function instead, like so...
function describeWithAssertion(
ae: boolean,
title: string,
f: any // this should be specified better
) {
const prevAE = assertionsEnabled;
return describe(
title,
() => {
beforeEach(() => assertionsEnabled = ae);
afterEach(() => assertionsEnabled = prevAE);
f();
}
);
}
This way, even if f contains async functionality, jest is worrying about it, not you.
I have a 3rd party module like this
class Test {
async doSomething() {}
}
export const testObject = new Test(); <--- I want to mock this part because constructor requires some input which I don't want to provide as it is not required for tests
another module which imports the above module
import { testObject } from 'module1';
function foo() {
testObject.doSomething()
}
Now I am trying to write unit tests like below
describe('test', ()=> {
test('', ()=> {
foo()
})
})
new Test() depends on some outside input which I don't want to provide so when i run tests, it fails because of missing input and I am not sure how to stop new Test() from being executed as is and instead a mock functions should be run instead
Try something like this. If you run this test it'll pass:
Class file.
class SoundPlayer {
foo: string
constructor() {
this.foo = 'bar'
}
playSoundFile(fileName: any) {
console.log('Playing sound file ' + fileName)
}
}
export const testObject = new SoundPlayer()
Function file
import { testObject } from './test'
export default function test() {
testObject.playSoundFile('testing')
}
Test file
import { testObject } from '../test'
import test from '../test1'
jest.mock('../test')
it('should run test', () => {
test()
expect(testObject.playSoundFile).toHaveBeenCalledTimes(1)
})
This is a very basic example and there are 4 different ways you can mock.
Check out the Jest documentation: https://jestjs.io/docs/es6-class-mocks#the-4-ways-to-create-an-es6-class-mock
I want to mock window.location.search.
config.test.js
import config from './config'
import { MULTIPLE_VIDEOS } from './Constants/flagKeys'
describe('', () => {
const flag = { [MULTIPLE_VIDEOS]: true }
global.window = Object.create(window)
Object.defineProperty(window, 'location', {
value: {}
})
afterAll(() => {
global.window = null
})
it('Mark query string flag as true', () => {
global.window.location.search = `?${MULTIPLE_VIDEOS}=true`
expect(config.flags).toEqual(flag)
})
})
config.js
export default { flags: getFlagsFromQueryString() }
function getFlagsFromQueryString () {
const queryString = qs.parse(window.location.search.slice(1))
const flags = {}
Object.entries(queryString).forEach(([name, value]) => {
flags[name] = value.toLowerCase() === 'true'
})
return flags
}
Though I set search value in location object before calling config.flags, I can't access it inside the function, It always returns empty string.
I want window.location.search.slice(1) to return ?multipleVideos=true inside getFlagsFromQueryString function instead of empty string, since I changed the value in the test file.
One thing I noticed, when I export the function and call in the test file it works.
For this type of complex uses. I would recommend you to create a Window service/util class and expose methods from there. easy to test and mock.
Sample:
// Window.js
class Window {
constructor(configs) {}
navigate(href) {
window.location.href = href;
}
}
export default new Window({});
Now you can easily mock Window.js. It is similar to DI pattern.