I have a simple mixin:
export const mixin = superclass => class extends superclass {
firstUpdated() {
super.firstUpdated();
this.dispatchEvent(new CustomEvent('my-event', {
bubbles: true,
composed: true,
detail: {
myEventTriggered: true,
},
}));
}
};
I want to test if my-event is fired. This is my test:
it('dispatches the custom event', async () => {
const tag = class extends mixin(LitElement) {};
const el = await fixture(`<${tag}></${tag}>`);
setTimeout(() => el.firstUpdated());
const { detail } = await oneEvent(el, 'my-event');
expect(detail.myEventTriggered).to.be.true;
});
This works as expected but I'm not sure about the setTimeout(() => el.firstUpdated()); line. Shouldn't firstUpdated be called after await fixture? I'm just following open-wc's testing guide.
Got an answer from #daKMor:
testing firstUpdated is a little tricky as as soon as you add it to the dom it's executed (with an arbitrary delay depending on the work your element is doing) https://lit-element.polymer-project.org/guide/lifecycle#firstupdated
what you can do is:
it('dispatches a custom event on firstUpdated', async () => {
const tag = class extends mixin(LitElement) {};
const el = fixtureSync(`<${tag}></${tag}>`);
const ev = await oneEvent(el, 'my-event');
expect(ev).to.exist;
});
it('dispatches a custom event on connectedCallback', async () => {
class Foo extends mixin(class {}) {};
const el = new Foo();
setTimeout(() => el.connectedCallback());
const ev = await oneEvent(el, 'my-event');
expect(ev).to.exist;
});
Note: this is untested code - if it works pls let me know and we could add that info and a little description to the faq. Maybe you could do a Pull Request?
For connectedCallback, since this callback is fired immediately after calling fixture, you can't catch it anymore. What you can do is define a new component and test its callback function in a setTimeout. An HTMLElement or LitElement is needed since oneEvent adds an Event Listener to the element.
it('dispatches a custom event on connectedCallback', () => {
const tag = defineCE(class extends mixin(LitElement) {});
const foo = document.createElement(`${tag}`);
setTimeout(() => foo.connectedCallback());
const ev = await oneEvent(foo, 'connected-callback');
expect(ev).to.exist;
});
Related
I have a test class that tests behavior of various HTTP methods in a Nest controller class. I am using Jest manual mocks to stub the behavior of various functions in the service class so that I do not have to rely on actual dependencies/services, eg. snowflake. I have a top level jest.mock() defined as follows which initializes the mocked version of the service class instead of the actual service class.The mocked service class is created inside mocks folder adjacent to the actual service class.
I am redefining the behavior of one of the mocked functions in the 'error scenario' describe block as shown in the code snippet below, for testing the error scenario . The test scenario : 'throws an error' is failing as it is still picking up the default mocked behavior. Any pointers or help is appreciated.
In short, I want to be able to define different mocked behavior for a single function of the same mocked class for various test scenarios.
Thanks
jest.mock('#modules/shipment-summary/shipment-summary.service');
describe('ShipmentSummaryController', () => {
let shipmentSummaryController: ShipmentSummaryController;
let shipmentSummaryService: ShipmentSummaryService;
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
imports: [],
controllers: [ShipmentSummaryController],
providers: [ShipmentSummaryService],
}).compile();
shipmentSummaryController = moduleRef.get<ShipmentSummaryController>(
ShipmentSummaryController,
);
shipmentSummaryService = moduleRef.get<ShipmentSummaryService>(
ShipmentSummaryService,
);
jest.clearAllMocks();
});
//All the tests inside this describe block work as expected
describe('valid shipment-mode scenario', () => {
describe('valid shipment modes for tenant', () => {
let modes: ShipmentMode[];
beforeEach(async () => {
modes = await shipmentSummaryController.getAllShipmentModes('256');
});
test('calls the service fn. with the correct arg', () => {
expect(shipmentSummaryService.getAvailableShipmentModes).toBeCalledWith(
'256',
);
});
test('all available shipment modes for 256 are returned', () => {
expect(modes).toEqual(validModeDropdown());
});
});
});
// redefining behavior of getAllshipmentModes() is not working
describe('error scenario', () => {
let modes: ShipmentMode[] = []
beforeEach(async () => {
modes = await shipmentSummaryController.getAllShipmentModes('256');
});
beforeAll(() => {
jest.clearAllMocks();
jest.mock('#modules/shipment-summary/shipment-summary.service.ts', () => {
return {
getAvailableShipmentModes: () => {
throw new Error('Test error');
},
}
});
});
test('throws an error', () => {
expect(() => shipmentSummaryController.getAllShipmentModes('256')).toThrow();
})
})
});
My mocked service class is as follows:
export const ShipmentSummaryService = jest.fn().mockReturnValue({
// Fn. to be mocked differently per test scenario.
getAvailableShipmentModes: jest.fn().mockResolvedValue(validModeDropdown()),
});
There are many ways of accomplishing this. The Nest docs outline a number of them. However, one of my preferred ways, useValue, is not as clear as it could be, so I'll added it here.
This example will also use jest in order to spy on a mock, changing its behavior depending on the test.
Imagine these two simple resources
Injectable();
export class SimpleService {
public sayHello(): string {
return "Hello, world!";
}
}
#Controller()
export class SimpleController {
constructor(
#Inject(SimpleService) private readonly simpleService: SimpleService
) {}
#Get()
public controllerSaysHello(): string {
return this.simpleService.sayHello();
}
}
Your tests could look something like this
describe("SimpleController", () => {
let controller: SimpleController;
const mockReturnValue = "Goodbye, world..",
mockSimpleService: SimpleService = {
sayHello: () => mockReturnValue,
};
beforeEach(() => {
jest.restoreAllMocks();
});
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
SimpleController,
{ provide: SimpleService, useValue: mockSimpleService },
],
}).compile();
controller = module.get(SimpleController);
});
test("default mockSimpleService", () => {
const result = controller.controllerSaysHello();
expect(result).toBe(mockReturnValue);
});
test("spied on mockSimpleService", () => {
const differentReturnValue = "Hallo!";
jest
.spyOn(mockSimpleService, "sayHello")
.mockReturnValue(differentReturnValue);
const result = controller.controllerSaysHello();
expect(result).toBe(differentReturnValue);
});
});
I am facing difficulty writing a unit test in jest for the code snippet below:
async addCronJob(props: IAddAndUpdateCronJobDetails) {
const {name, individualSchedule} = props;
const parsedCronTime = convertDateAndTimeToCron(
individualSchedule.timeOfRun,
individualSchedule.dateOfrun
)
const {jobType, dateOfRun, id, timeOfRun} = individualSchedule;
const newJob = new CronJob(
parsedCronTime,
async () => {
return this.sqsService.getSqsApproval({
//some properties
}).then(() => {
//some logic
})
},
null,
false,
'Asia/Singapore'
)
this.schedulerRegistry.addCronJob(name, newJob)
newJob.start()
}
And here is my unit test:
//at the top
jest.mock('cron', () => {
const mScheduleJob = {start: jest.fn(), stop: jest.fn()};
const mCronJob = jest.fn(() => mScheduleJob);
return {CronJob: mCronJob}
})
***************
describe('addCronJob', () => {
it('should add a new cron job', async (done) => {
const testFn = jest.fn();
const parsedCronTime = convertDateAndTimeToCron(
mockSchedule.timeOfRun,
mockSchedule.dateOfrun
)
const testCronJob = new CronJob(
parsedCronTime,
testFn,
null,
false,
'Asia/Singapore'
);
return dynamicCronService.addCron({//properties}).then(() => {
expect(CronJob).toHaveBeenCalledWith(//properties);
expect(testCronJob.start).toBeCalledTimes(1);
done()
})
})
})
The above test passes without error. However, it is unable to test for this block of async code within the cron job itself:
async () => {
return this.sqsService.getSqsApproval({
//some properties
}).then(() => {
//some logic
})
}
Anyone have an idea how to test the above block of code?
Thanks!
Probably late to the party, but I struggled with this myself and wanted to share my solution:
Method in service
async addCronJob(taskName: string, cronEx: string, onTickCallback: () => void | Promise<void>): Promise<void> {
const newJob = new CronJob(cronEx, onTickCallback);
this.schedulerRegistry.addCronJob(taskName, newJob);
newJob.start();
}
Test
it('should create cronJob', async () => {
await service.addCronJob(jobName, testCronExpression, callbackFunction);
expect(schedulerRegistryMock.addCronJob).toHaveBeenCalledWith(jobName, expect.any(CronJob));
jest.advanceTimersByTime(60 * 60 * 1000);
expect(callbackFunction).toHaveBeenCalled();
});
Instead of creating a test cronjob with a test function, I had to mock the actual function I'm expecting the cronjob to call on tick (in your case, that should be getSqsApproval I believe). Then I expected schedulerRegistry.addCronJob to be called with any CronJob, since I can't know the specific job. Creating a new job and expecting it here won't work.
Finally, I advanced the time by 1 hour because my testCronExpression was 0 * * * *. You should advance the time depending on the cron expression you use for testing.
Expecting the callbackFunction to have been called after the time passed (virtually) worked for me.
Hope this helps!
I'm using NodeJS and a MongoDB. I have this simple function for returning a generic property of a document ...
import mongoose, { Document, Schema } from "mongoose";
export interface IMyObject extends Document {
...
}
...
export async function getProperty(
req: CustomRequest<MyDto>,
res: Response,
next: NextFunction
): Promise<void> {
const {
params: { propertyName, code },
} = req;
try {
const my_obj = await MyObject.findOne({ code });
const propertyValue = my_obj ? my_obj.get(propertyName) : null;
if (propertyValue) {
res.status(200).json(propertyValue);
...
I'm struggling to figure out how to test this function. In particular, how do I mock an instance of my object that's compatible with the "get" method? I tried this
it("Should return the proper result", async () => {
const myObject = {
name: "jon",
};
MyObject.findOne = jest.fn().mockResolvedValue(myObject.name);
const resp = await superTestApp.get(
"/getProperty/name/7777"
);
expect(resp.status).toBe(StatusCodes.OK);
expect(resp.body).toEqual("happy");
but this fails with
TypeError: my_object.get is not a function
You would need to spy your object and its methods. Something like:
import MyObject from '..';
const mockedData = {
get: (v) => v
};
let objectSpy;
// spy the method and set the mocked data before all tests execution
beforeAll(() => {
objectSpy = jest.spyOn(MyObject, 'findOne');
objectSpy.mockReturnValue(mockedData);
});
// clear the mock the method after all tests execution
afterAll(() => {
objectSpy.mockClear();
});
// call your method, should be returning same content as `mockedData` const
test('init', () => {
const response = MyObject.findOne();
expect(response.get('whatever')).toEqual(mockedData.get('whatever'));
});
Right now I am writing an unit test for this kind of situation like below:
public div: HTMLDivElement;
public currentEvent: EventType;
public listenToRender() {
this.adsService.render.filter((event: EventType) => {
return this.div.id === event.slot.getSlotElementId();
}).subscribe((event: EventType) => {
let custom_event = new CustomEvent('render', {
detail: event
});
this.currentEvent= event;
});
}
During the unit test, I mock the render with subject, but I don't know how I can make it pass the filter
return this.div.id === event.slot.getSlotElementId();
and go to the subscribe function.
class MockAdsService {
render = new Subject();
}
class MockEventType {
name: 'test_event';
slot: {
getSlotElementId = function() {return 'test_id'}
};
}
describe('test', () => {
let mockAdsService: MockAdsService,
mockEventType: MockEventType;
beforeEach(() => {
mockAdsService = new MockAdsService();
mockEventType = new MockEventType();
});
it('listenToRender fired correctly', () => {
mockAdsService.render.next(mockEventType);
component.listenToRender();
expect(component.currentEvent).toEqual(mockEventType);
});
});
Do I need to set up something in subject.next for passing the filter?
It's very simple. You're subscribing your component after your event has already happened. It's too late for cold observable. Just switch render.next() and component.listenToRender() calls and everything should work just fine:
it('listenToRender fired correctly', () => {
component.listenToRender();
mockAdsService.render.next(mockEventType);
expect(component.currentEvent).toEqual(mockEventType);
});
Given a simple component that subscribes to the activated route query params in ngOnInit:
export class FooComponent implements OnInit {
private queryParams: any;
constructor(
private activatedRoute: ActivatedRoute
) { }
ngOnInit() {
this.activatedRoute.queryParams.subscribe(params => this.queryParams = params);
}
active(foo: number): boolean {
return this.queryParams['foo'] &&
foo === +this.queryParams['foo'];
}
}
The active function should return true when the foo query param is present and its value matches the supplied parameter.
In the accompanying unit tests for this component, I want to change the value of the query params within each it block to test the query param not being present, matching the parameter and not matching the parameter.
describe('FooComponent', () => {
let component: FooComponent;
let fixture: ComponentFixture<FooComponent>;
let activatedRoute: ActivatedRoute;
class MockActivatedRoute {
queryParams = Observable.of({});
}
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [FooComponent],
providers: [
{ provide: ActivatedRoute, useClass: MockActivatedRoute }
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FooComponent);
component = fixture.componentInstance;
fixture.detectChanges();
activatedRoute = fixture.debugElement.injector.get(ActivatedRoute);
});
describe('active', () => {
it('should return false if the foo query param is not present', () => {
activatedRoute.queryParams = Observable.of({});
let result = component.active(100);
expect(result).toBe(false);
});
it('should return false if the foo query param does not match the supplied parameter', () => {
activatedRoute.queryParams = Observable.of({ foo: '500' });
let result = component.active(100);
expect(result).toBe(false);
});
it('should return true if the foo query param does not match the supplied parameter', () => {
activatedRoute.queryParams = Observable.of({ foo: '500' });
let result = component.active(500);
expect(result).toBe(true);
});
});
});
Rather the value of the private queryParams member of the FooComponent class does not update within each it block. I've tried the various methods of async, fixture.whenStable(), and fakeAsync/tick.
How do I update the value of the subscription for each unit test?
It's because you are assigning a new Observable, but the client is already subscribed to the first Observable. This happens because ngOnInit is called when you first call fixture.detectChanges(). If you waited to called fixture.detectChanges() after you assign the new Observable to the queryParams, then that Observable would be used.
Another option (maybe preferred) is to instead of using an Observable, you can use a Subject. With this, you can control when data is emitted, and what to emit.
import { Subject } from 'rxjs/Subject'
import { fakeAsync, tick } from
class MockActivatedRoute {
queryParams = new Subject<any>();
}
let route: MockActivatedRoute;
beforeEach(() => {
/* configure */
route = <MockActivatedRoute>TestBed.get(ActivatedRoute);
})
it('', fakeAsync(() => {
route.queryParams.next(newparams); // emit something
tick(); // wait for resolution
fixture.detectChanges(); // detect changes (for ui)
expect(...)
}))
I say this options might be preferred as it allows for emitting multiple values in the same test.