How can I test a function that use a promise? - unit-testing

how can I test a function that call a promise ?
click() {
this.todoService.getUsers().then(
(resp) => {
this.users = resp;
},
(reject) => {
console.log(reject);
}
);
}
I tried this , but the console return undeefined
it('should get all users', () => {
const users =[....]
todoServiceSpy.getUsers.and.returnValue(of(users).toPromise());
component.click();
console.log('component.users', component.users);
expect(component.users).toEqual(users);
I'm new in testings

You can use async/fixture.whenStable() to wait for the promises in queue to complete before proceeding.
// add async here
it('should get all users', async () => {
const users =[....]
todoServiceSpy.getUsers.and.returnValue(of(users).toPromise());
component.click();
// !! wait for all promises in queue to complete before proceeding
await fixture.whenStable();
console.log('component.users', component.users);
// do assertions
expect(component.users).toEqual(users);
You can also use fakeAsync/tick. I suggest you read up on these methodologies.
It explains some of it here: https://www.digitalocean.com/community/tutorials/angular-testing-async-fakeasync.

Related

How to mock function with callback and return type in Jasmine

I'm trying to mock function that has a stream return type and callback as arg. I get timeout because the call back function never get called.
This is the function I'm trying to test:
public setServerUrls(): Observable<void> {
const obs: Observable<void> = new Observable((observer: Observer<void>) => {
const stream: ClientWritableStream<Util.Server> = this.fileTransferClient.setURLs((error: ServiceError, response) => {
if (error) {
observer.error(`TransferManager Electron: unable to set server urls error: ${error.message}.`);
}
observer.next();
observer.complete();
logger.info(`TransferManager Electron: setting URLs completed.`);
});
const transferRequest = new Util.Server();
transferRequest
.setIp('localhost')
.setTransferport('9092')
.setUuid('4020a522-81fe-4996-b637-0620ae656d29');
stream.write(transferRequest);
stream.end();
});
return obs;
}
My jasmine setup:
const settingStream = {
write: () => { },
end: () => { }
};
const callBack = () => {
return;
};
const f = function (callback: Function): any {
return settingStream;
};
mockFileTransferClient = jasmine.createSpyObj('FileTransferClient', {
subscribe: () => mockFileTransferStream,
uploadFile: () => duplexStream,
setURLs: f(callBack)
});
mockFileTransferClientWrapper.createNewFileTransferClient.and.returnValue(mockFileTransferClient as any);
// Question here... does not work!
mockFileTransferClient.setURLs.and.returnValue(settingStream);
test:
it('should set urls', done => {
transferManager.setServerUrls()
.subscribe(
x => {
expect(x).toBeDefined();
done();
}
);
});
This is Elctron + grpc functionality test.
Test timeout because it never gets into the callback and observable never completes.
I'm not sure how to mock setURLs with return value AND callback.
You have to first mock your setUrls to return a resolved observable.
I think what you need here is to use fakeAsync() functionality as you are dealing with async test. Using Tick with fakeAsync ensures that all pending asynchronous activities will finish. In your case it will wait for setTimeout.
Note that you should also inject the setServerUrls using TestBed.inject...
So I think your tests should look like this
it("should .....", fakeAsync(() => {
// Arrange - mock functions
// Act - Make a call to the function
// Use tick() after making call to the function which will flush your setTimeouts
// Assert
}));
For more information, see the fakeAsync documentation

Check if a stubbed getter function has been called with sinon spy

I am using firebase admin and I am trying to write some unit tests for my code.
Since admin is injected in my function I figured I could mock a very simple object like this:
admin = {
get auth () {
return {
updateUser: () => {
return true;
},
createUser: () => {
return true;
},
getUser: () => {
throw Error('no user');
}
};
}
};
Then in a particular test I can stub the functions. Here is what I have done so far:
// stubbed functions
sinon.stub(admin, 'auth').get(() => () => ({
updateUser: () => ({ called: true }),
getUser: () => (userRecord),
createUser: () => ({ called: false })
}));
and those are working fine (I can see with my logs).
However in my test I would also want to check if createUser has been called at all.
I thought I could set up a spy on the createUser function, but so far I can't really get it to work.
Here is what I have been trying (with a bunch of variation always failing):
it.only('should update a user', async () => {
const userRecord = mockData
sinon.stub(admin, 'auth').get(() => () => ({
updateUser: () => ({ called: true }),
getUser: () => (userRecord),
createUser: () => ({ called: false })
}));
const spy = sinon.spy(admin, 'auth', ['get']); // this is not working
const user = await upsertUser(data, firestore, admin);
expect(user).toEqual(data.userDataForAuth); // this one is ok
sinon.assert.calledOnce(spy.get); // this throws an error
});
the bit of code I am trying to test (which is the upsert function is this:
// in my test exisiting user is not null (the stub `getUser` is returning a object
if (existingUser != null) {
try {
await admin.auth().updateUser(uid, userDataForAuth);
return userDataForAuth;
} catch (error) {
console.log('error', error);
throw Error('error updating user');
}
}
I am not even sure this is the best approach, happy to change it if there is a better one!

Skip implementation in Jest

Currently I have the following piece of code:
function handleConnection(socket: Socket): void {
info(`Connection started with socketId: ${socket.id}`)
socket.on("joinRoom", (request: string) => handleJoinRoom(socket, request));
socket.on("shareData", (request: IShareDataRequest) => handleShareData(socket, request));
socket.on("disconnect", () => handleDisconnect(socket));
}
I want to write a test for each and every events like joinRoom, shareData and disconnect. To isolae the tests I want to only test the second socket.on("shareData", () => ...) call and skip the first socket.on("joinRoom", () => ...) call. I've tried with multiple mockImplementationOnce methods with no success.
The test I wrote:
it('should emit data to room', () => {
const listenMock = listen();
const socketMock = {
on: jest.fn()
.mockImplementationOnce(() => null)
.mockImplementationOnce((event: string, callback: Function) => callback({ roomId: "abcdefg" })),
to: jest.fn().mockReturnValue({
emit: jest.fn()
})
}
jest.spyOn(listenMock, "on").mockImplementationOnce((event: string, callback: Function) => callback(socketMock))
startSocket();
expect(socketMock.to).toHaveBeenCalledWith("abcdefg");
expect(socketMock.to().emit).toHaveBeenCalledWith("receiveData", expect.any(Object));
})
ShareData function:
function handleShareData(socket: Socket, request: IShareDataRequest): void {
socket.to(request.roomId).emit("receiveData", request);
}
I would really appreciate if anyone could help me out with this.
You can try the following approach:
// define the mockSocket
const mockSocket = {
// without any implementation
on: jest.fn()
};
describe("connection handler", () => {
// I personally like separating the test setup
// in beforeAll blocks
beforeAll(() => {
handleConnection(mockSocket);
});
// you can write assertions that .on
// should have been called for each event
// with a callback
describe.each(["joinRoom", "shareData", "disconnect"])(
"for event %s",
event => {
it("should attach joinRoom handlers", () => {
expect(mockSocket.on.mock.calls).toEqual(
expect.arrayContaining([[event, expect.any(Function)]])
);
});
}
);
describe("joinRoom handler", () => {
beforeAll(() => {
// jest mock functions keep the calls internally
// and you can use them to find the call with
// the event that you need and retrieve the callback
const [_, joinRoomHandler] = mockSocket.on.mock.calls.find(
([eventName]) => eventName === "joinRoom"
);
// and then call it
joinRoomHandler("mockRequestString");
});
it("should handle the event properly", () => {
// your joinRoom handler assertions would go here
});
});
});

How to write a unit test with data fetching, that alters data based on respone in vuejs?

I am trying to write a unit test for a function that does an async call, but it doesnt seem to alter the data prop, maybe I am doing something wrong.
Check the code below:
getSomething() {
MyService.getThis().then(
response => {
this.status = true;
}
).catch(error => {})
}
TestCase:
describe('test', () => {
beforeEach(() => {
// To ignore the created hook, but this doesnt work, any idea?
spyOn(CustomerData, 'created');
spyOn(MyService, 'getThis').and.returnValue(Promise.resolve(list));
});
wrapper = shallowMount(MyComponent, {
propsData: {
data: {}
},
});
it('should work', () => {
wrapper.vm.getSomething();
expect(wrapper.vm.status).toBeTruthy();
});
});
}
The status should be true, but it is false, but if I print the value of status in the getSomething() function it is indeed true. I have no idea what the issue can be.
update:
In the test case I wrote
it('should work', async () => {
await wrapper.vm.getSomething();
expect(wrapper.vm.status).toBeTruthy();
}
and this seems to work. Is this a good way to solve it? Would love to hear other solutions.
Also I am very interested if it is possible to ignore the created hook, I havent been able to figure that out yet.
Code that running inside getSomething() is asynchronous. MyService.getThis() returns promise, and its execution takes time, in case if you fetching some data from remote serivce.
So first of all you need to return promise from getSomething()
getSomething() {
return MyService.getThis()
.then(response => { this.status = true; })
.catch(error => {})
}
And inside the test you need to return promise outside, to let jest know that your test is asynchronous.
it('should work', () => {
return wrapper.vm.getSomething().then(() => {
expect(wrapper.vm.status).toBeTruthy();
});
});
Or as you mentioned in the edited part you can use async version:
it('should work', async () => {
await getSomething();
expect(wrapper.vm.status).toBeTruthy();
});

Jest test redux action with thunk doesn't cover statemets

Hello i have been trying to test a function with thunk and all the test passes but can't figure it out why the coverage doesn't not update or the test function does not cover the statement.
This is my function:
export const setFinished = (campaignId, userId, actionId, callback) => {
return async (dispatch, getState) => {
await axios.post(`http://bazuca.com:9000/campaigns/${campaignId}/progress`, {
userId,
actionId
}, { headers: { token: getState().app.token } })
.then((response) => {
})
.catch((error) => {
})
callback();
}
}
This is my last test (I have done like 3 different types and cant get the coverage to work)
describe("setFinished", () => {
it("works", () => {
const dispatch = jest.fn();
const callback = jest.fn(() => 'callback');
const getState = jest.fn();
let a = setFinished(1, 1, 1, callback)
expect(a).toHaveBeenCalledWith(1, 1, 1, callback);
a(dispatch, getState);
expect(callback).toHaveBeenCalled();
});
});
and i just get this in the coverage:
Maybe im doing it wrong? or should use another library?
There might be some things missing in your test setup. Especially the way you're making an assertion about the dispatch mock looks unusual. Without going into too much detail, just consider the following:
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { setFinished } from 'path/to/your/actions';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe('setFinished', () => {
it('works', () => {
// You have to make sure axios calls are mocked out properly
// at this point. I don't have a snippet handy for this so I
// left it out. But it would be similar to the following:
axios.mockImplementationOnce(() => ({
// Let the promise return whatever your response is for a
// positive test case
post: () => Promise.resolve({ isFinished: true })
}));
const expected = [
// I'm assuming something like this is dispatched in the
// .then handler of your action:
{ type: 'SET_FINISHED_SUCCESS' }
];
const store = mockStore({});
// Mock some arguments here
return store.dispatch(setFinished(1, 2, 3, () => null))
.then(() => expect(store.getActions()).toEqual(expected));
});
});
If axios is mocked out correctly, this will definitely achieve 100% coverage for this action if you also add a negative test case for the catch block.