Stub function returning promise - javascript

I'm trying to unit test the following controller:
export class MyController extends BaseController {
constructor() {
super();
this.repository = new MyRepository();
}
public getData(req: Request, res: Response, next: NextFunction) {
this.repository.getData(req.params.param1).then((result) => {
return this.ok(req, res, result.resources) // calls ok() method from base controller
}, (err: Error) => {
next(err)
});
}
}
I would like to stub the MyRepository.getData which returns a Promise<MyResult>
I also want to stub the BaseController.ok method to ensure it's called with the data returned from repo.
Here is my test:
it("should call the repository", (done) => {
var mockReq = httpMocks.createRequest();
var mockResp = httpMocks.createResponse();
const mockNext: NextFunction = stub();
mockReq.params.param1 = "value1";
let sampleResult = new MyResult();
const getDataStub = stub(MyRepository.prototype, "getData").resolves(sampleResult);
const okStub = stub(MyController.prototype, "ok");
new MyController().getData(mockReq, mockResp, mockNext);
expect(getDataStub).to.have.been.calledWith("value1"); // passes ok
expect(okStub).to.have.been.called; // fails
done()
});
The test fails when checking, if the okStub has been called at least once. If I debug the code I can see that the BaseController.ok is actually called, but after the evaluation in test.

Looks like code inside of getData.then gone to another cycle at event loop.
Try to place done() inside of this.ok like this
stub(MyController.prototype, "ok").callsFake(() => {
done();
});

Related

Angular 9 + jest : unit test, mock a promise and check method called then

I need help, I don't know how to mock a promise and check a method is called after in the then() part.
My code looks like this, when I click on the save button of my form :
// File : myComponent.ts
save() {
const myObject = new MyObject({field: this.form.value.field});
this.myService.saveObject(myObject).then(() => { // I'd like to mock this
this.closeDialog(true);
}, error => {
this.otherFunction(error);
});
}
// File : myService.ts
saveOject(myObject: MyObject): Promise<any> {
return this.myApi.save(myOject).toPromise().then(res => res);
}
// File : myApi.ts
save(myObject: MyObject) {
return this.http.post('url, myObject);
}
I'm trying to test this function and I would like to mock (or stub ? I don't know the difference) the saveObject function for when the promise is resolved, and the case it's not.
My actuel test file looks like this:
describe('MyComponent', () => {
let component: MyComponent;
let fixture: ComponentFixture<MyComponent>;
let myService: MyService;
beforeEach(async (() => {
TestBed.configureTestingModule(
).compileComponents();
myService = TestBed.inject(MyService);
}
beforeEach(() => {
fixture = TestBed.createComponent(MyComponent);
component = fixture.componentInstance;
fixture.detectChanges();
spyOn(myService, 'saveOject').and.returnValue(new Promise(resolve => resolve()));
});
it('should call closeDialog method when save form is successful', () => {
const spyCloseDialog = jest.spyOn(component, 'closeDialog');
component.save();
fixture.detectChanges(); // It's a test, I don't know if it's useful
expect(spyCloseDialog).toHaveBeenCalledTimes(1); // It's 0 because I don't know how to be in the then part of my function
});
}
Can somebody help me ?
Sincerely
There are two options to choose from:
1) use fakeAsync, for example:
it('should call closeDialog method when save form is successful', fakeAsync(() => {
const spyCloseDialog = jest.spyOn(component, 'closeDialog');
component.save();
tick(50);
expect(spyCloseDialog).toHaveBeenCalledTimes(1);
}));
2) put your expect inside then, for example
component.save().then(() => expect(spyCloseDialog).toHaveBeenCalledTimes(1));
In your test you should import HttpClientTestingModule, so that test runs succesfully and no error is thrown when angular tries to fire up a http call.

How do I resolve Promise in mocked function?

I'm trying to test method which returns Promise and resolves in another method which I want to mock.
I can test it without mocking but I don't want to because of credentials which wouldn't work on another machine.
Here's my Service code (PollyService.ts):
#Service()
export class PollyService {
#Inject()
private polly: Polly; // aws-sdk Polly
url: string;
getSpeech(body: any) {
return new Promise((resolve) => {
let voice: string = (voices as any)[body.code];
let params = {
exampleParam: example
};
this.polly.synthesizeSpeech(params, (error: any, data: any) => {
if (error) throw error;
else {
resolve(data);
}
});
});
}
}
Here's test (app.spec.ts)
describe('PollyService', () => {
afterEach(() => {
Container.reset();
});
it ('Should return data', async () => {
const event: any = {
body: {
sentence: "potato",
code: "en"
}
};
let polly = Container.get(Polly); // aws-sdk polly
spyOn(polly, 'synthesizeSpeech'); // and here I want to resolve Promise
await Container.get(PollyService).getSpeech(event.body);
});
});
When I am mocking that without resolving promise there is error:
Error: Timeout - Async function did not complete within 5000ms (set by jasmine.DEFAULT_TIMEOUT_INTERVAL)
How can i simulate resolving Promise in tests?
If you want to create a promise which is already resolved, you can simply use Promise.resolve(someData)
In your case it should be something like this:
spyOn(polly, 'synthesizeSpeech').and.returnValue(Promise.resolve('mockedData'));
Note: You get the timeout error message because you are neither returning the promise in the end of your function nor make use of the done callback.
either do:
...
return await Container.get(PollyService).getSpeech(event.body);
or
describe('PollyService', () => {
afterEach(() => {
Container.reset();
});
it ('Should return data', async (done) => { // <- done is the callback
const event: any = {
body: {
sentence: "potato",
code: "en"
}
};
let polly = Container.get(Polly);
spyOn(polly, 'synthesizeSpeech');
await Container.get(PollyService).getSpeech(event.body);
done(); // <- calling it will tell jasmine your test is over
});
});
Usually either you choose to return the promise or use the callback. I am not sure on how jasmine mix async methods and the callback in the same method but it should work. More information here
You should extend the spyOn with and.returnValue as follows:
const data = ... // define the data to be returned by synthesizeSpeech
spyOn(polly, 'synthesizeSpeech').and.returnValue(Promise.resolve(data));

How to test a helper functions used in controller in Sinon.js

I am using sinon.js to test my API.
I would like to test the order from my helper functions that are being called.
controller.js
exports.controllerFunction = async (req, res) => {
const function1Results = await function1(paramm);
const function2Results = await function2(param, function1Results);
return res.send(function2Results);
};
helpers.js
exports.function1 = function(param) {
return param;
}
exports.function2 = function(param, func) {
return param;
}
unitTest.js
const controller = require('./controller.js')
const helpers = require('./helpers.js')
describe('Unit test cycle', () => {
beforeEach(() => {
// Spies
sinon.spy(controller, 'controllerFunction');
sinon.spy(helpers, 'function1');
sinon.spy(helpers, 'function2');
// Function calls
controller.controllerFunction(this.req, this.res)
})
afterEach(() => {
sinon.restore();
})
this.req = {}
this.res = {}
it('should call getAvailability', (done) => {
expect(controller.controllerFunction.calledOnce).to.be.true
expect(helpers.function1.calledOnce).to.be.true
expect(helpers.function2.calledOnce).to.be.true
});
})
expect(controller.controllerFunction.calledOnce).to.be.true
is returning in as true
expect(helpers.function1.calledOnce).to.be.true
expect(helpers.function2.calledOnce).to.be.true
and is coming in as false.
Because my helper functions are being used in the controller they should be called as well, yet they are not.
So how do I test if my helper functions are being called as well when the controller is tested?
I will try to give a shot. Since your function is async I think your block for testing should be made to await for it.
I recommend moving the function call inside the it block of commands. beforeEach is usually used for setup, afterEach is used to clear some data/mocks (also known as tear).
Try
it('should call getAvailability', async (done) => {
// When
await controller.controllerFunction(this.req, this.res)
// Assert
expect(controller.controllerFunction.calledOnce).to.be.true
expect(helpers.function1.calledOnce).to.be.true
expect(helpers.function2.calledOnce).to.be.true
done && done()
});
Don't forget to remove the function call from beforeEach.

Jest test the resolve reject callback

I have this function which calls an util function for api calls. The util function resolves or rejects based on the api result.
Now, I need to unit test the callback functions which has the following structure.
`theClassMethod : () => {
return utilMethod().then(
result => { this.functionOne() //Test this function is called },
error => { this.functionTwo() //Test this function is called }
)
}`
The util method returns a promise like below:
utilFunc = (data :string) :Promise<ResultData[]> => {
return new Promise(async (resolve, reject) => {
try{
resolve(data)
}catch{
reject(error)
}
})
}
https://codesandbox.io/s/vjnwy1zw75?fontsize=14
What I tried:
Mocked the util method to resolve/reject. Call the class method and do assertions. It doesn't work and the test always passes as a false positive.
I have spend much time looking for a similar problem. Most questions here are to test the code like:
theClassMethod : () => { utilMethod.then().catch()}
The problem I am trying to solve is to test the resolve, reject callbacks in the then block then(function1, function2). That the code block inside function1 has to be tested that it calls some intended functions.
The approach you are describing (mocking utilMethod to resolve/reject) is a good approach.
Here is a simple working example to get you started:
Note: I implemented functionOne as a class method and functionTwo as an instance property to show how to spy on both types of functions:
util.js
export const utilMethod = async () => 'original';
code.js
import { utilMethod } from './util';
export class MyClass {
functionOne() { } // <= class method
functionTwo = () => { } // <= instance property
theClassMethod() {
return utilMethod().then(
result => { this.functionOne() },
error => { this.functionTwo() }
);
}
}
code.test.js
import { MyClass } from './code';
import * as util from './util';
test('theClassMethod', async () => {
const mock = jest.spyOn(util, 'utilMethod');
const instance = new MyClass();
const functionOneSpy = jest.spyOn(MyClass.prototype, 'functionOne'); // <= class method
const functionTwoSpy = jest.spyOn(instance, 'functionTwo'); // <= instance property
mock.mockResolvedValue('mocked value'); // <= mock it to resolve
await instance.theClassMethod();
expect(functionOneSpy).toHaveBeenCalled(); // Success!
mock.mockRejectedValue(new Error('something bad happened')); // <= mock it to reject
await instance.theClassMethod();
expect(functionTwoSpy).toHaveBeenCalled(); // Success!
});

Stubing a class call from another function

I have 2 files controller.js and entity.js which interact with each other. I am testing controller.js, and it creates an instance of entity.js (class) and use one of its functions. How can I stub/mock/spy the call and the return of that method?
controller.js
const controller= async (req, res) => {
try {
...
const entity = new Entity({
...
});
const validation = await entity.validate();
...
return res.send()
}
} catch (error) {
return res.send(error)
}
};
Entity.js
class Entity{
constructor() {
...
}
...
async validate() {
...
return response;
}
}
Any idea how to test controller.js using supertest, sinon and chai?
Sinon will happily stub the function. Since it's a class method you just need to be sure to stub the function on the prototype:
const controller = async (req, res) => {
const entity = new Entity();
const validation = await entity.validate();
console.log(validation)
};
class Entity{
constructor() {}
async validate() {
return "real function";
}
}
// stub it
let stub = sinon.stub(Entity.prototype, 'validate')
stub.returns('stubbed function')
controller()
<script src="https://cdnjs.cloudflare.com/ajax/libs/sinon.js/7.1.1/sinon.min.js"></script>
This solution uses Ava (but you should be able to adapt to Mocha easily). However I'm more familiar with testdouble. If you have no success with sinon (I'm sure you will), here's an alternative that you may want to consider.
So if we have burrito.js:
module.exports = class {
eat() {
return '🌯';
}
};
And lunch.js:
var Burrito = require('./burrito');
module.exports = () => (new Burrito()).eat();
Then in your test:
const td = require('testdouble');
const test = require('ava');
test('swap burrito', t => {
td.replace('./burrito', class FakeBurrito {
eat() {
return '🌮';
}
});
const lunch = require('./lunch');
t.is(lunch(), '🌮'); // PASS
t.is(lunch(), '🌯'); // FAIL
});
The key is to require your dependency (a burrito) before your subject under test (your lunch) requires it, so that you have time to fake it.

Categories