Unit Test Observables and check count of subscribers in Angular - unit-testing

I have subject and observable -
export class WebSocket{
private _WebSocketSubject: Subject<any>;
public OWebSockConn$: Observable<any>;
constructor(){
this._WebSocketSubject = new Subject<JSON>();
this.OWebSockConn$ = this._WebSocketSubject.asObservable();
}
public receiveData = () => {
this._WebSocketSubject$.next("hi");
});
}
ReceiveData is called to push data. In Unit test i want to know the count of subscribers for this subject and i want to check whether data i receive is correct or not. So this is my unit test case -
Approach 1:
it('Find_Observanles_AndData_IsCorrect', async ((done) => {
myWebSocket.receiveData();
expect((myWebSocket as any).OWebSockConn$.observers.length).toBe(1);
myWebSocket.invokeFromWebSockConn$.subscribe((data) => {
console.log("Data reached");
expect(data).not.tobe(null);
done();
});
}));
Approach 2:
it('Find_Observanles_AndData_IsCorrect', async(inject( [WebSocket], ( websocket ) => {
myWebSocket.receiveData();
expect((myWebSocket as any).OWebSockConn$.observers.length).toBe(1);
websocket.OWebSockConn.subscribe(result => expect(data).not.tobe(null));
}));
It is not going inside subscribe and console is also not printing, and my subscriber count is 0 although it is subscribed in another service, i tried two approaches i don't know where is the issue, please help a noobie.
Tried approach mentioned here as well -
Unit testing an observable in Angular 2

Related

How to mock Fastify plugin

I am trying to write unit testing for fastify application which also has custom fastify plugin.
Is there a way we can mock fastify plugin? I tried mocking using Jest and Sinon without much success.
Giorgios link to the file is broken, the mocks folder is now absent from the master branch. I dug the commit history to something around the time of his answer and I found a commit with the folder still there. I leave it here for those who will come in the future!
This is what works for me
Setup your plugin according to Fastify docs https://www.fastify.io/docs/latest/Reference/Plugins/
// establishDbConnection.ts
import fp from 'fastify-plugin';
import {FastifyInstance, FastifyPluginAsync} from 'fastify';
import { initDbConnection } from './myDbImpl';
const establishDbConnection: FastifyPluginAsync = async (fastify: FastifyInstance, opts) => {
fastify.addHook('onReady', async () => {
await initDbConnection()
});
};
export default fp(establishDbConnection);
mock the plugin with jest, make sure you wrap the mock function in fp() so that Fastify recognizes it as a plugin.
// myTest.ts
import fp from 'fastify-plugin';
const mockPlugin = fp(async () => jest.fn());
jest.mock('../../../fastifyPlugin/establishDbConnection', (() => {
return mockPlugin;
}));
Your question is a bit generic but if you are using Jest it must be enough for mocking a fastify plugin. You can take a look in this repo and more specifically this file . This is a mock file of fastify and you add the registered plugins and in the specific example addCustomHealthCheck and then in your test files you can just call jest.mock('fastify').
You do not give a specific use case and there are lot of reasons you might want to mock a plugin. The nature of the plugin to be mocked is important to giving a good answer. Because I don't know that specific information I will show how to mock a plugin that creates a decorator that stores data that can be retrieved with fastify.decorator-name. This is a common use case for plugins that connect to databases or store other widely needed variables.
In the below case, the goal is to test a query function that queries a db; a plugin stores the connection information via a fastify decorator. So, in order to unit test the query we specifically need to mock the client data for the connection.
First create an instance of fastify. Next, set up a mock to return the desired fake response. Then, instead of registering the component with fastify (which you could also do), simply decorate the required variables directly with mock information.
Here is the function to be tested. We need to mock a plugin for a database which creates a fastify decorator called db. Specifically, in the below case the function to be tested uses db.client:
const fastify = require("fastify")({ //this is here to gather logs
logger: {
level: "debug",
file: "./logs/combined.log"
}
});
const HOURS_FROM_LOADDATE = "12";
const allDataQuery = `
SELECT *
FROM todo_items
WHERE a."LOAD_DATE" > current_date - interval $1 hour
`;
const queryAll = async (db) => {
return await sendQuery(db, allDataQuery, [HOURS_FROM_LOADDATE]);
};
//send query to db and receive data
const sendQuery = async (db, query, queryParams) => {
var res = {};
try {
const todo_items = await db.client.any(query, queryParams);
res = todo_items;
} catch (e) {
fastify.log.error(e);
}
return res;
};
module.exports = {
queryByAsv
};
Following is the test case. We will mock db.client from the db plugin:
const { queryAll } = require("../src/query");
const any = {
any: jest.fn(() => {
return "mock response";
})
};
describe("should return db query", () => {
beforeAll(async () => {
// set up fastify for test instance
fastify_test = require("fastify")({
logger: {
level: "debug",
file: "./logs/combined.log",
prettyPrint: true
}
});
});
test("test Query All", async () => {
// mock client
const clientPromise = {
client: any
};
//
fastify_test.decorate("db", clientPromise);
const qAll = await queryAll(fastify_test.db);
expect(qAll).toEqual("mock response");
});
});

Mocking a Flow.js interface with Jest?

How might a Flow.js interface be mocked with Jest? To my surprise, I haven't found this issue addressed anywhere.
I'm fairly new to both, but the only (untested) option I see is to create a class that inherits from the interface and then mock the implementing class. This seems quite cumbersome and I don't believe I could place the implementing classes (which are what would actually be mocked) inside the __mocks__ folders expected by Jest and still get the expected behavior.
Any suggestions? Is there a more appropriate mocking tool?
Update
Why do I want to create a mock for an interface? This code intends to have a clean separation of the domain and implementation layers, with the domain classes using Flow interfaces for all injected dependencies. I want to test these domain classes. Using a mocking tool could ideally allow me to more easily and expressively modify the behavior of the mocked services and confirm that the domain class being tested is making the appropriate calls to these mocked services.
Here's a simplified example of a class that I would be testing in this scenario. UpdateResources would be the class under test, while ResourceServer and ResourceRepository are interfaces for services that I would like to mock and 'spy' upon:
// #flow
import type { ResourceServer } from '../ResourceServer';
import type { ResourceRepository } from '../ResourceRepository';
/**
* Use case for updating resources
*/
export default class UpdateResources {
resourceServer: ResourceServer;
resourceRepository: ResourceRepository;
constructor(resourceServer: ResourceServer, resourceRepository: ResourceRepository) {
this.resourceServer = resourceServer;
this.resourceRepository = resourceRepository;
}
async execute(): Promise<boolean> {
const updatesAvailable = await this.resourceServer.checkForUpdates();
if (updatesAvailable) {
const resources = await this.resourceServer.getResources();
await this.resourceRepository.saveAll(resources);
}
return updatesAvailable;
}
}
A solution
The approach I've arrived at which seems to work quite well for my purposes is to create a mock implementation of the interface in the __mocks__ directory what exposes jest.fn objects for all implemented methods. I then instantiate these mock implementations with new and skip any use of jest.mock().
__mocks__/MockResourceServer.js
import type { ResourceServer } from '../ResourceServer';
export default class MockResourceServer implements ResourceServer {
getResources = jest.fn(() => Promise.resolve({}));
checkForUpodates = jest.fn(() => Promise.resolve(true));
}
__mocks__/MockResourceRepository.js
import type { ResourceRepository } from '../ResourceRepository';
export default class MockResourceRepository implements ResourceRepository {
saveAll = jest.fn(() => Promise.resolve());
}
__tests__/UpdateResources.test.js
import UpdateResources from '../UpdateResources';
import MockResourceRepository from '../../__mocks__/MockResourceRepository';
import MockResourceServer from '../../__mocks__/MockResourceServer';
describe('UpdateResources', () => {
describe('execute()', () => {
const mockResourceServer = new MockResourceServer();
const mockResourceRepository = new MockResourceRepository();
beforeEach(() => {
jest.clearAllMocks();
});
it('should check the ResourceServer for updates', async () => {
const updateResources = new UpdateResources(mockResourceServer, mockResourceRepository);
await updateResources.execute();
expect(mockResourceServer.checkForUpdates).toHaveBeenCalledTimes(1);
});
it('should save to ResourceRepository if updates are available', async () => {
mockResourceServer.load.mockResolvedValue(true);
const updateResources = new UpdateResources(mockResourceServer, mockResourceRepository);
await updateResources.execute();
expect(mockResourceRepository.saveAll).toHaveBeenCalledTimes(1);
});
it('should NOT save to ResourceRepository if NO updates are available', async () => {
mockResourceServer.load.mockResolvedValue(false);
const updateResources = new UpdateResources(mockResourceServer, mockResourceRepository);
await updateResources.execute();
expect(mockResourceRepository.saveAll).not.toHaveBeenCalled();
});
});
});
If anyone can offer any improvements, I'm open!
The thing is, you don't actually need to mock an implementation of an interface. The purpose of a mock is to 'look like' the real thing, but if you already have an interface that says what the real thing should look like, any implementation that conforms to the interface will automatically serve equally well as a mock. In fact, from the point of view of the typechecker, there won't be a different between the 'real' and the 'mock' implementation.
Personally what I like to do is to create a mock implementation that can be constructed by feeding it mock responses. Then it can be reused in any test case by constructing it directly in that test case with the exact responses it should provide. I.e., you 'script' the mock with what it should say by injecting the responses at the time of construction. The difference between it and your mocking implementation is that if it doesn't have a response, it throws a exception and fails the test. Here's an article I wrote that shows this method: https://dev.to/yawaramin/interfaces-for-scaling-and-testing-javascript-1daj
With this technique, a test case might look like this:
it('should save to ResourceRepository if updates are available', async () => {
const updateResources = new UpdateResources(
new MockResourceServer({
checkForUpdates: [true],
getResources: [{}],
}),
new MockResourceRepository({
saveAll: [undefined],
}),
);
const result = await updateResources.execute();
expect(result).toBeTruthy();
});
What I like about these mocks is that all the responses are explicit, and show you the sequence of calls that are happening.

Angular2 async unit testing with jasmine

I'm writing an angular2 app and having trouble understanding how to write tests for async code using jasmine. For whatever reason, I'm not seeing a lot of examples that seem terribly applicable to my situation.
I'm currently trying to test a service (not a component) that has a async dependency on another service. I'm not 100% sure at what point in the test it's valid to check the results of the async call. Can you just call expect() inside the async handler for your service?
service.foo()
.then((data) => {
//do I check the results in here?
expect(data).toEqual({ a: 1, b: 2 });
expect(mockDep.get).toHaveBeenCalled();
});
Here is the full test.
import { TestBed, inject } from '#angular/core/testing';
import { MyService } from './my.service.ts';
import { MyDependency } from './dependency.service.ts';
class MockDependency {
doSomething(): Promise<any> {
throw Error('not implemented');
};
}
describe('some tests', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
MyService,
{
provide: MyDependency, useClass: MockDependency
}
]
});
});
});
it('should do something', inject([MyService, MyDependency], (service: MyService, mockDep: MyDependency) => {
spyOn(mockDep, 'doSomething').and.callFake(function () {
return Promise.resolve({ a: 1, b: 2 });
});
service.foo()
.then((data) => {
//do I check the results in here?
expect(data).toEqual({ a: 1, b: 2 });
expect(mockDep.get).toHaveBeenCalled();
});
}));
There's two aspects of dealing with async tests that you have to concern yourself about if you want to ensure your tests are actually reliable.
First, you have to ensure that if a result is retrieved asynchronously, that you wait until the result is available before you try to test for it.
Hence, if the asynchronous result is a promise, for eg, you can put your expect in your then handler, just as you indicated in your question.
The second issue that you have to concern yourself with is forcing your test itself to wait for your expectations to execute before giving a positive (or negative) result. If you don't deal with this, you can have cases where your expectation fails, but because your test did not wait for your asynchronous action to complete before finishing, the test reports a false positive.
There are several ways of 'making your test wait'.
The pure jasmine way is to pass a done handler into your it function. Then jasmine will wait until that done handler is called before considering the test complete.
eg.
it('tests an async action', (done) => {
asyncAction().then(result => {
expect(result).toEqual(true);
done();
});
});
However, angular's testing framework adds two other options to this. The first is easier to grasp if you are comfortable with async programming.
it('tests an async action', async(() => {
asyncAction().then(result => {
expect(result).toEqual(true);
});
}));
in this case, you basically wrap your test handler in an async function. This function will force the test to wait for any async results (eg promises, observables etc) to return a result, before allowing the test to complete.
The second method is to use fakeAsync, which allows you to hide the async nature of your test entirely.
it('tests an async action', fakeAsync(() => {
let myResult;
asyncAction().then(result => {
myResult = result;
});
tick(); <--- force all async actions to complete
expect(myResult).toEqual(true);
}));
fakeAsync hooks into all async functions and allows you to treat them as synchronous. You can use the tick() function to 'force your test to wait' for async tasks to complete before continuing.
(It's not quite doing that, but conceptually, you can think of it that way).
See the Angular docs to learn more

Testing basic navigation in Angular 2 with fakeAsync() instead of Jasmine's done()

I currently have a working test using Gherkin steps and Jasmine async done() for basic navigation as such:
User_Navigates_To_URL(urlPath: string) {
return this.router.navigateByUrl(urlPath);
}
User_Will_Be_On_URL(expectedPath: string) {
expect(this.location.path()).toBe(expectedPath);
}
it('Scenario: User should be able to navigate', (done) => {
When.User_Navigates_To_URL('/associates/your-team').then(() => {
Then.User_Will_Be_On_URL('/associates/your-team');
});
done();
});
But what I'm trying to accomplish is to write this test using fakeAsync instead of jasmine's done() method. This way all async actions will be resolved in the zone and I wont have to nest the assertion step as a callback of the promise. Therefore, I'm attempting to do something like so:
it('Scenario: User should be able to navigate', <any>fakeAsync(() => {
When.User_Navigates_To_URL('/associates/your-team');
tick();
Then.User_Will_Be_On_URL('/associates/your-team');
}));
After weeks of research, the only thing close I found helpful was this question: Does fakeAsync guarantee promise completion after tick/flushMicroservice. But even when I've tried to implement my navigation promise in his snippet, it never resolves.
User_Navigates_To_URL(urlPath: string) {
let currNav:Promise<Router> = this.router.navigateByUrl(urlPath);
let handleNavigation = function (p:Promise<Router>) {
p.then(() => {
console.log('navigation complete')
});
};
let p = Promise.resolve(currNav);
handleNavigation(p);
tick();
}
This is my first question on here so please let me know if my quesiton is confusing or if I need to provide any more details.

Angular2 testing logic before .subscribe

I am currently writing unit tests for one of my components. In particular, I have login(): void function. Here's the simplified logic:
login(): void {
this.showSpinner = true;
this.userService.login(loginData)
.subscribe(result => {
this.showSpinner = false;
}
)
}
I am struggling to write a test that checks that showSpinner property gets set to true before calling the userService.login.
Here's my test:
it('should display the spinner when the form is being saved',
inject([TestComponentBuilder], fakeAsync((tcb: any) => {
createComponent(tcb).then((fixture:ComponentFixture<any>) => {
fixture.componentInstance.login();
expect(fixture.componentInstance.showSpinner).toBe(true);
tick();
});
})));
});
And this test fails, because .subscribe gets resolved / run immediately (i tried commenting out this.showSpinner = false in my component, and the test passed).
In my userService mock, I have the following, for the login method mock:
this.loginSpy = this.spy('login').andReturn(Observable.of(this));
Where this is mockUserService.
I am confident that I am mocking userService and specifically the login method on the userService correctly, as I have other tests for this component that behave correctly.
I have also tried returning Observable.of(this).delay(1) from my spy and then calling tick(1) in my test. However that results in inconsistent behaviour in that sometimes my tests pass, but other times i get an error saying:
Error: 1 periodic timer(s) still in the queue.
How can I test the logic that precedes .subscribe()?
After more consideration I have realized that my current code does not abide by the single responsibility principle. This thought came from the fact that everyone is always repeating that you should "Refactor hard to test code".
With that in mind, I have moved all the logic that needed to be done before the call to userService.login is being made - into its own separate function. Which essentially results in:
login():void {
this.userService.login(this.loginData)
.subscribe(result => {
this.showSpinner = false;
});
}
formSubmit(): void {
this.showSpinner = true;
this.login();
}
This logic is now much easier to test.
HOWEVER we need to remember to add a spy on our login() method when we are testing formSubmit(), as if we don't, formSubmit() will simply make a call to login(), which will again complete synchronously and we will have the same problem. So my new and final test for this feature is:
it('should display the spinner when the form is being saved',
inject([TestComponentBuilder], fakeAsync((tcb: any) => {
createComponent(tcb).then((fixture:ComponentFixture<any>) => {
var loginSpy = spyOn(fixture.componentInstance, 'login');
fixture.componentInstance.formSubmit();
expect(fixture.componentInstance.showSpinner).toBe(true);
});
})));
});