how do I unit test this function which handles observables - rxjs

I have created this function because for all the requests my application sends out using http.post, this is how different parts handle the response. So rather than duplicating the code, I thought to create a function. But I am unable to figure out how to unit test this function.
private editAnswerSubject: Subject<Result>;
subscribeToReturnedObservable(observable:Observable<any>, subject:Subject<Result>) {
observable.subscribe((res) => {
const ev = <HttpEvent<any>>(res);
if (ev.type === HttpEventType.Response) {
const isResponseStructureOK: boolean = this.helper.validateServerResponseStructure(ev.body);
if (isResponseStructureOK) {
const response: ServerResponseAPI = ev.body;
subject.next(new Result(response.result, response['additional-info']));
} else {
subject.next(new Result(messages.error, messages.invalidStructureOfResponse));
}
}
},
(error: ServerResponseAPI) => {
const errorMessage: string = this.helper.userFriendlyErrorMessage(error);
subject.next(new Result(messages.error, errorMessage));
},
() => { // observable complete
});
}
editAnswer(answer: Answer): any {
const observable = this.bs.editAnswer(answer)
this.subscribeToReturnedObservable(observable,this.editAnswerSubject);
}
The test I have written so far is
describe('subscribeToReturnedObservable tests:', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [QuestionManagementService, HelperService, WebToBackendInterfaceService, AuthService, HttpClient, HttpHandler]
});
});
fit('should call send next value for the subject is the response from the server is ok', () => {
const questionService:QuestionManagementService = TestBed.get(QuestionManagementService);
const body = {"result":"success", "additional-info":"some additional info"};
const receivedHttpEvent = new HttpResponse({body:body});
let observable = new Observable();
spyOn(observable,'subscribe').and.returnValue(receivedHttpEvent);
spyOn(questionService['editQuestionSubject'],'next');
questionService.subscribeToReturnedObservable(observable,questionService['editQuestionSubject']);
observable.subscribe();
expect(questionService['editQuestionSubject'].next).toHaveBeenCalled();
});
});
But it get error Expected spy next to have been called.

I did this (hoping that it is the right way). The scope of testing is to check that the Subject's next is called correctly. So create an Observable using of and let the code flow from there.
fit('should call send next value for the subject is the response from the server is ok', () => {
const questionService:QuestionManagementService = TestBed.get(QuestionManagementService);
const helperService:HelperService = TestBed.get(HelperService);
const body = {"result":"success", "additional-info":"some additional info"};
const receivedHttpEvent = new HttpResponse({body:body});
const expectedResult = new Result('success', 'some additional info');
spyOn(helperService,'validateServerResponseStructure').and.returnValue(true);
let observable = of(receivedHttpEvent);
spyOn(questionService['editQuestionSubject'],'next');
questionService.subscribeToReturnedObservable(observable,questionService['editQuestionSubject']);
expect(questionService['editQuestionSubject'].next).toHaveBeenCalledWith(expectedResult);
});

Related

Deno oak websocket must have a method that returns an async iterator

I am trying to build up a WebSocket with oak (not the native deno one).
The following code is how I build the server.
import {Application, Router, Context, send } from "https://deno.land/x/oak#v10.6.0/mod.ts";
const runWS = async (ctx: Context, next: () => Promise<unknown>) => {
try{
const ws = await ctx.upgrade();
ws.onopen = () => {
chatConnection(ws);
};
ws.onclose = () => { console.log('Disconnected from the client!');};
}catch{await next();}
}
let sockets = new Map<string, WebSocket>();
const chatConnection = async (ws: WebSocket) => {
console.log('new websocket, ws: ',ws);
const uid = globalThis.crypto.randomUUID();
sockets.set(uid,ws);
console.log('socket: ',sockets);
for await (const ev of ws){
console.log('ev: ', ev);
}
}
export const wsRoutes = new Router()
.get('/ws', runWS);
But in the for loop (at the end), for ws it says Type 'WebSocket' must have a '[Symbol.asyncIterator]()' method that returns an async iterator.. What's the deal with this and how to fix it?
The error message is providing you with useful information: the WebSocket is not AsyncIterable, which means that it cannot be used with a for await...of loop.
Here is the type documentation for WebSocket in Deno. It is (for the most part) the same as the WHATWG standard WebSocket that is documented on MDN.
If your intention is to respond to incoming message events, you'll need to attach an event listener:
webSocket.addEventListener("message", (messageEvent) => {
// Do something in response to each message event
});
Additional:
Here's an observation based on the code you've shown, but not in response to your question:
It's probably more ergonomic to store the sockets as the keys of your map, and the associated state data in the values. (This is the inverse of what you've shown). Here's an example of why:
import {
Router,
type RouterMiddleware,
} from "https://deno.land/x/oak#v10.6.0/mod.ts";
// You seem to want to log data to the console.
// This function will help you easily log only certain properties of objects:
/**
* Functional implementation of the type utility
* [`Pick<Type, Keys>`](https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys)
*/
function pick<T, K extends keyof T>(
obj: T,
keys: readonly K[],
): Pick<T, K> {
const result = {} as Pick<T, K>;
for (const key of keys) result[key] = obj[key];
return result;
}
type SocketData = { id: string };
const socketMap = new Map<WebSocket, SocketData>();
// Do something when a connection is opened
function handleOpen(ev: Event, ws: WebSocket) {
const socketData: SocketData = { id: window.crypto.randomUUID() };
socketMap.set(ws, socketData);
console.log({
event: pick(ev, ["type"]),
socketData,
});
}
// Do something when an error occurs
function handleError(ev: Event, ws: WebSocket) {
const socketData = socketMap.get(ws);
console.log({
event: pick(ev, ["type"]),
socketData,
});
socketMap.delete(ws);
}
// Do something when a connection is closed
function handleClose(ev: CloseEvent, ws: WebSocket) {
ev.code; // number
ev.reason; // string
ev.wasClean; // boolean
const socketData = socketMap.get(ws);
console.log({
event: pick(ev, ["type", "code", "reason", "wasClean"]),
socketData,
});
socketMap.delete(ws);
}
// Do something when a message is received
// Change `unknown` to the type of message payloads used in your application.
// (for example, JSON messages are `string`)
function handleMessage(ev: MessageEvent<unknown>, ws: WebSocket) {
ev.data; // unknown
ev.lastEventId; // string
ev.ports; // readonly MessagePort[]
const socketData = socketMap.get(ws);
if (socketData) {
socketData.id; // string
}
console.log({
event: pick(ev, ["type", "data", "lastEventId", "ports"]),
socketData,
});
}
const webSocketMiddleware: RouterMiddleware<"/ws"> = async (ctx, next) => {
const ws = ctx.upgrade();
ws.addEventListener("open", (ev) => handleOpen(ev, ws));
ws.addEventListener("error", (ev) => handleError(ev, ws));
ws.addEventListener("close", (ev) => handleClose(ev, ws));
ws.addEventListener("message", (ev) => handleMessage(ev, ws));
await next();
};
export const router = new Router();
router.get("/ws", webSocketMiddleware);
This is my updated code. It avoids the problem entirely
import {Application, Router, Context, send } from "https://deno.land/x/oak#v10.6.0/mod.ts";
interface BroadcastObj{
name: string,
mssg: string
}
const runWS = async (ctx: Context, next: () => Promise<unknown>) => {
if(!ctx.isUpgradable){
ctx.throw(501);
}
const uid = globalThis.crypto.randomUUID();
try{
const ws = await ctx.upgrade();
ws.onopen = () => {
chatConnection(ws);
};
ws.onmessage = (m) => {
let mssg = m.data as string;
if(typeof(mssg) === 'string'){
chatMessage(JSON.parse(mssg));
}
};
ws.onerror = (e) => {console.log('error occured: ', e);};
ws.onclose = () => { chatDisconnect(uid);};
}catch{await next();}
}
let sockets = new Map<string, WebSocket>();
const chatConnection = async (ws: WebSocket, uid: string) => {
await sockets.set(uid,ws);
}
const chatMessage = async (msg: BroadcastObj) => {
await sockets.forEach((ws: WebSocket) => {
ws.send(JSON.stringify(msg));
});
}
const chatDisconnect = async (uid: string) => {
await sockets.delete(uid);
}
export const wsRoutes = new Router()
.get('/ws', runWS);

Is there a way to spy on a Promise constructor?

I have this function
public pick(config?: FilePickerConfig): Promise<FilePickerResult> {
return new Promise<FilePickerResult>(resolve => {
this.pickWithCallbacks(resolve, resolve, config);
});
}
I want to test if the call to this.pickWithCallbacks has as first and second parameter the resolve parameter of the function.
Is there a way to do this in jest or jasmine? I have tried to spy on window, 'Promise' but it does not work.
Edit: It is not a depulicate of Spying on a constructor using Jasmine because that is what I have tried and did not work.
I have tried this:
const dummyResolve = () => { };
const promiseSpy = spyOn(window, 'Promise').and.callFake((dummyResolve)=>{});
const pickWithCallbacksSpy = spyOn(sut, 'pickWithCallbacks');
sut.pick();
expect(pickWithCallbacksSpy).toHaveBeenCalledWith(dummyResolve, dummyResolve, undefined);
So finally I just left the Promise do his thing and I captured the resolve callback
test('on success should call pickWithCallbacks with the resolve function of a promise', (done) => {
const cordovaExecSpy = spyOn(sut, 'pickWithCallbacks');
const dummyReturn = {};
sut.pick().then(obtained => {
expect(obtained).toBe(dummyReturn);
done();
});
const capturedOnSucess = cordovaExecSpy.calls.mostRecent().args[0];
capturedOnSucess(dummyReturn);
});
test('on Error should call pickWithCallbacks with the resolve function of a promise', (done) => {
const cordovaExecSpy = spyOn(sut, 'pickWithCallbacks');
const dummyReturn = {};
sut.pick().then(obtained => {
expect(obtained).toBe(dummyReturn);
done();
});
const capturedOnError = cordovaExecSpy.calls.mostRecent().args[1];
capturedOnError(dummyReturn);
});

How to use sinon to mock a series of knex call

Given a myknex.js:
export default { return require('knex')({client:'mysql',connection:{//conn}})}
and I want to write a unit test for the following function:
async function get({ userId }) {
return await myknex('users')
.where({ id: userId })
.returning('*');
}
The unit test looks like:
const sinon = require('sinon');
const sandbox = sinon.createSandbox();
const { myknex } = require('./myknex');
it('no record', async () => {
sandbox
.stub(myknex('users'), 'executeQuery').resolves([]);
const result = await myrepo.get({ userId: 1 });
const expectedResult = [];
expect(result).to.deep.equal(expectedResult);
});
I got an error message about:
TypeError: Cannot stub non-existent own property executeQuery
How can I mock the chained myknex calls?
Since you have myknex.js export a function of knex, we need to use proxyquire to mock this in test file.
const chai = require('chai');
const expect = chai.expect;
const sinon = require('sinon');
const proxyquire = require('proxyquire'); // include proxyquire
const expectedResult = [];
const knexQuery = {
where: sinon.stub().returnsThis(), // because we want to call `returning` afterwards
returning: sinon.stub().resolves(expectedResult) // resolve it as promise
}
const myknexStub = sinon.stub().returns(knexQuery);
const myrepo = proxyquire('./src', { './myknex': myknexStub }); // your source file and stub myknex here
describe(('Test My Module'), () => {
it('no record', async () => {
const result = await myrepo.get({ userId: 1 });
// many options to unit test the function
expect(myknexStub.calledWith('users')).to.be.ok;
expect(knexQuery.where.calledWith({ id: 1 })).to.be.ok;
expect(knexQuery.returning.calledWith('*')).to.be.ok;
expect(knexQuery.returning.calledAfter(knexQuery.where)).to.be.ok;
expect(result).to.deep.equal(expectedResult);
});
});
Hope it helps

React redux Thunk - Making two ajax call one after the other with the result from the first ajax call using fetch API

I am trying to make two ajax call one after the other , i.e with the result of the first call data , i am making the second call. I am trying to use thunk , but it is not happening , i am getting errors.
actions.js
const fetchedStateNameSucess = (json) => {
return {
type: 'FETCH_STATE_NAME_SUCCESS',
json
}
}
const fetchProvidersDetailsSuccess = (providersData) => {
return {
type: 'FETCH_PROVIDER_DETAILS_SUCCESS',
providersData
}
}
export const fetchProvidersDetails = (providerId) => {
return (dispatch) => {
const providerUrl = `http://someUrl`;
const stateClientCountUrl = `http://someUrl/${state}`;
fetch(providerUrl)
.then(response => response.json())
.then(json => dispatch(fetchProvidersDetailsSuccess(json)))
.then((stateValue = json[0].stateVal)
fetch(stateClientCountUrl)
dispatch(fetchedStateNameSucess(response)));
};
}
In the above call , fetch(providerUrl) , i am getting response where i am getting the stateval , how to use that in making the second call to fetch(stateClientCountUrl) which takes stateval as a parameter.
As Miguel said you can do your second query in .then() clause as well as you can use async/await syntax, something like this:
export const fetchProvidersDetails = providerId => {
return async dispatch => {
const providerUrl = `http://someUrl`;
try {
const response = await fetch(providerUrl);
const json = await response.json();
dispatch(fetchProvidersDetailsSuccess(json))
const stateClientCountUrl = `http://someUrl/${json[0].stateVal}`;
const response2 = await fetch(stateClientCountUrl);
const json2 = await response2.json();
dispatch(fetchedStateNameSucess(json2));
} catch (error) {
console.log('Error', error);
}
}
If you want to use values from the first call response for the second fetch call, you need to do the second fetch after the first one has succeeded, more or less like this:
export const fetchProvidersDetails = (providerId) => {
return (dispatch) => {
const providerUrl = `http://someUrl`;
const stateClientCountUrl = `http://someUrl/${state}`;
fetch(providerUrl)
.then(response => response.json())
.then(json => {
dispatch(fetchProvidersDetailsSuccess(json));
const stateValue = json[0].stateVal;
fetch(stateClientCountUrl)
.then(response => dispatch(fetchProvidersDetailsSuccess(response)));
})
}
Don't forget to add error handling there, both for HTTP status codes and for JavaScript errors (by adding corresponding catch clauses).

chai-as-promised should.eventually.equal not passing

I am trying to write a minimum working example of chai-as-promised in order to understand how it is working when testing functions that return a promise.
I have the following function:
simple.test = async (input) => {
return input;
};
and the following test function:
chai.use(sinonChai);
chai.use(chaiAsPromised);
const { expect } = chai;
const should = chai.should();
describe('#Test', () => {
it('test', () => {
expect(simple.test(1)).should.eventually.equal(1);
});
});
However, testing this results in the test not passing, but in a very long error, which is pasted here: https://pastebin.com/fppecStx
Question: Is there something wrong about the code, or what seems to be the problem here?
First: Your mixing expect and should. If you want to use should for assertion, you don't need expect.
Second: To tell mocha that a test is async you have to either call done, return a Promise or use async/await.
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const sinonChai = require('sinon-chai');
const should = chai.should();
chai.use(sinonChai);
chai.use(chaiAsPromised);
// Function to test
const simple = {
test: async (input) => {
return input;
}
}
// Test
describe('#Test', () => {
it('test', () => {
return simple.test(1).should.eventually.equal(1);
});
});

Resources