Mock method from class 'b' that is called in class 'a' - javascript

I'm trying to test a method in the class test.controller.ts, this method also happens to call a method from a different class that is being tested seperately, so I want to mock that call.
Here is an example setup to show what i'm trying to do.
TestController.test.ts
import {TestController} from "../../src/controllers/test.controller";
import {TestService} from "../../src/services/test.service";
describe("Test TestController", () => {
test("exampleController", () => {
jest.mock('../../src/services/test.service');
let testService = new TestService();
let testController = new TestController();
// Mock testService.exampleService so it returns 2.
let result = testController.exampleController();
expect(result).resolves.toBe(2);
});
});
test.controller.ts
import {TestService} from "../services/test.service";
export class TestController {
private testService: TestService;
constructor() {
this.testService = new TestService();
}
public async exampleController() {
return await this.testService.exampleService();
}
}
test.service.ts
export class TestService {
public async exampleService() {
return 1;
}
}
How do I mock the method 'exampleService' so that the call to the method 'exampleController' from test.controller.ts uses this mocked version?

You need to mock the testService field of your TestController class.
But with your current code, that isn't possible as it's a private member.
This is why using dependency injection is preferred, so we need to change your constructor to this,
constructor(testService: TestService) {
this.testService = testService;
}
Instead of instantiating testService within the constructor, we are now passing an object of TestService so that it is easy to mock.
And then you can test it like this,
import {TestController} from "./controller";
import {TestService} from "./service";
jest.mock('./service.ts')
describe("Test TestController", () => {
test("exampleController", async () => {
let testService = new TestService();
jest.spyOn(testService, 'exampleService').mockResolvedValue(2)
let testController = new TestController(testService);
let result = await testController.exampleController();
expect(result).toBe(2);
});
});
Here you create an object of TestService.
Then you create a spy on the exampleService method of the testService object and mock its resolved value to return 2.
Then you pass it to TestController's constructor, this is called dependency injection, which makes it easier to test.
And then you proceed to assert as per your expectations.

For mocking functions you can use the sinon library. It can be done as shown below:
let testService = new TestService();
sinon.stub(testService, "exampleService").callsFake(function fakeFn() {
return 2;
});

If you need to specify the return values and full-on replace the implementation of a function with a mock function, it can be done with jest.fn and mockImplementationOnce method on mock functions.
TestController.test.ts should look like
import {TestController} from "../../src/controllers/test.controller";
import {TestService} from "../../src/services/test.service";
describe("Test TestController", () => {
test("exampleController", () => {
jest.mock('../../src/services/test.service');
let testService = new TestService();
let testController = new TestController();
// Mock testService.exampleService so it returns 2.
testService.exampleService = jest.fn().mockImplementationOnce(() => Promise.resolve(2));
let result = testController.exampleController();
expect(result).resolves.toBe(2);
});
});

Related

test class method that return another class

I want to test whether the main class method of getService return the correct new class based on the correct conditional
// main.js
import ServiceA from './serviceA'
import ServiceB from './serviceB'
class Main {
constructor(){}
getService(serviceName){
switch(serviceName){
case 'serviceA':
return new ServiceA()
case 'serviceB':
return new ServiceB()
default
return null
}
}
}
Would it be possible to test that the returned class is correct? I tried something like this
import Main from './Main'
describe('Main method', () => {
describe('getService given ServiceA', () => {
it.skip('should return an instantiate of ServiceA class', function () {
const main = new Main();
const getService = spy(main, 'getService');
main.getService('serviceA');
expect(getService).to.be.an.instanceOf(ServiceA);
});
});
There shouldn't be a need to spy on getService(). Since you are just testing the input and output of the getService() without any dependencies. Spying would allow to see call count and arguments passed to the spy to see if it was called inside the method you are testing. How you had it is mostly correct as it is.
import Main from './Main'
describe('Main method', () => {
describe('getService given ServiceA', () => {
it('should return an instantiate of ServiceA class', function () {
const main = new Main();
const result = main.getService('serviceA');
expect(result).to.be.an.instanceOf(ServiceA);
});
});

Testing: spyOn Helper class in angular

Is it possible to spyOn helper class? In the below code, StatefulPatternService.init() is calling a WebSocketHelper.
I would like to spyOn WebSocketHelper and mock the subscribeFn
export class WebSocketHelper{
private url: string;
constructor(url){
this.url = url;
init();
}
init(){
// init websocket & other login
}
}
#Injectable({
providedIn: 'root'
})
export class StatefulPatternService {
constructor(){}
private callback(_data){ }
init(){
let wsHelper = new WebSocketHelper('/subscribe/topic'); // <-- How to spyOn???
wsHelper.subscribeFn = this.callback;
// ...
}
}
If spyOn won't be possible, then how it can be re-written so that this test can be covered?
Your challenge will be getting a hold of 'wsHelper' in order to spy on it. One thought: can you refactor to make wsHelper a class-scope variable instead? Then you could spyOn it when you get the service in the test suite, for example, something like:
service = TestBed.get(StatefulPatternService);
let spy = spyOn(service.wsHelper, 'subscribeFn');
Update
From the comments to my answer it looks like what you are really trying to do is verify that the constructor was called with the proper url. Since you are saving that in a class variable, there should be no need to spy on the constructor, but rather just test the value of the saved variable. As I mentioned in the comments, to do this you will need two things: to make wsHelper a class level variable, and to add a method on the WebSocketHelper class that returns the value of the private variable 'url' so you can test it. I've set up a stackblitz to demonstrate what I'm talking about here: STACKBLITZ Here is a snippet from that stackblitz:
describe('MyService', () => {
let myService: StatefulPatternService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [/* any imports needed */],
providers: [ StatefulPatternService ]
});
myService = TestBed.get(StatefulPatternService);
});
it('should be createable', () => {
expect(myService).toBeTruthy();
});
it('should construct wsHelper properly', () => {
myService.init();
expect(myService.wsHelper.get_url()).toEqual('/subscribe/topic');
})
});

How to test class instance inside a function with Jest

I have the following hypothetical scenario:
// file MyClass.js in an external package
class MyClass {
myfunc = () => {
// do something
}
}
// file in my project
function myFunctionToBeTested() {
const instance = new MyClass()
instance.myFunc()
}
I need to create a test with Jest that makes sure instance.myFunc was called
One of the option is to replace MyClass module with mock implementation
const mockmyfunc = jest.fn()
jest.mock("path/to/external/package/MyClass", () => {
return jest.fn().mockImplementation(() => {
return {myfunc: mockmyfunc}
})
})
And then write following test
it("Test myfunc called in functionToBeTested", () => {
functionToBeTested()
expect(mockmyfunc).toHaveBeenCalled()
})
Note that this is not the only way, you can dive into https://facebook.github.io/jest/docs/en/es6-class-mocks.html for other alternatives.
Update
If the myfunc would be an actual function (which i guess is not an option since it's external package?)
export class MyClass {
myFunc() {
// do smth
}
}
and you would not need to replace the implementation, you could be using jest's automock
import MyClass from "path/to/external/package/MyClass"
jest.mock("path/to/external/package/MyClass")
it("Test myfunc called in functionToBeTested", () => {
functionToBeTested()
const mockMyFunc = MyClass.mock.instances[0].myFunc
expect(mockMyFunc).toHaveBeenCalled()
})
you can mock out the class and assign the default export of that file to a variable as follows:
jest.mock('../../utils/api/api');
const FakeClass = require('../someFile.js').default;
then access calls to a function on your mock class like this:
FakeClass.prototype.myFunc.mock.calls

ES6 Singleton vs Instantiating a Class once

I see patterns which make use of a singleton pattern using ES6 classes and I am wondering why I would use them as opposed to just instantiating the class at the bottom of the file and exporting the instance. Is there some kind of negative drawback to doing this? For example:
ES6 Exporting Instance:
import Constants from '../constants';
class _API {
constructor() {
this.url = Constants.API_URL;
}
getCities() {
return fetch(this.url, { method: 'get' })
.then(response => response.json());
}
}
const API = new _API();
export default API;
Usage:
import API from './services/api-service'
What is the difference from using the following Singleton pattern? Are there any reasons for using one from the other? Im actually more curious to know if the first example I gave can have issues that I am not aware of.
Singleton Pattern:
import Constants from '../constants';
let instance = null;
class API {
constructor() {
if(!instance){
instance = this;
}
this.url = Constants.API_URL;
return instance;
}
getCities() {
return fetch(this.url, { method: 'get' })
.then(response => response.json());
}
}
export default API;
Usage:
import API from './services/api-service';
let api = new API()
I would recommend neither. This is totally overcomplicated. If you only need one object, do not use the class syntax! Just go for
import Constants from '../constants';
export default {
url: Constants.API_URL,
getCities() {
return fetch(this.url, { method: 'get' }).then(response => response.json());
}
};
import API from './services/api-service'
or even simpler
import Constants from '../constants';
export const url = Constants.API_URL;
export function getCities() {
return fetch(url, { method: 'get' }).then(response => response.json());
}
import * as API from './services/api-service'
The difference is if you want to test things.
Say you have api.spec.js test file. And that your API thingy has one dependency, like those Constants.
Specifically, constructor in both your versions takes one parameter, your Constants import.
So your constructor looks like this:
class API {
constructor(constants) {
this.API_URL = constants.API_URL;
}
...
}
// single-instance method first
import API from './api';
describe('Single Instance', () => {
it('should take Constants as parameter', () => {
const mockConstants = {
API_URL: "fake_url"
}
const api = new API(mockConstants); // all good, you provided mock here.
});
});
Now, with exporting instance, there's no mocking.
import API from './api';
describe('Singleton', () => {
it('should let us mock the constants somehow', () => {
const mockConstants = {
API_URL: "fake_url"
}
// erm... now what?
});
});
With instantiated object exported, you can't (easily and sanely) change its behavior.
Both are different ways.
Exporting a class like as below
const APIobj = new _API();
export default APIobj; //shortcut=> export new _API()
and then importing like as below in multiple files would point to same instance and a way of creating Singleton pattern.
import APIobj from './services/api-service'
Whereas the other way of exporting the class directly is not singleton as in the file where we are importing we need to new up the class and this will create a separate instance for each newing up
Exporting class only:
export default API;
Importing class and newing up
import API from './services/api-service';
let api = new API()
Another reason to use Singleton Pattern is in some frameworks (like Polymer 1.0) you can't use export syntax.
That's why second option (Singleton pattern) is more useful, for me.
Hope it helps.
Maybe I'm late, because this question is written in 2018, but it still appear in the top of result page when search for js singleton class and I think that it still not have the right answer even if the others ways works. but don't create a class instance.
And this is my way to create a JS singleton class:
class TestClass {
static getInstance(dev = true) {
if (!TestClass.instance) {
console.log('Creating new instance');
Object.defineProperty(TestClass, 'instance', {
value: new TestClass(dev),
writable : false,
enumerable : true,
configurable : false
});
} else {
console.log('Instance already exist');
}
return TestClass.instance;
}
random;
constructor() {
this.random = Math.floor(Math.random() * 99999);
}
}
const instance1 = TestClass.getInstance();
console.log(`The value of random var of instance1 is: ${instance1.random}`);
const instance2 = TestClass.getInstance();
console.log(`The value of random var of instance2 is: ${instance2.random}`);
And this is the result of execution of this code.
Creating new instance
The value of random var of instance1 is: 14929
Instance already exist
The value of random var of instance2 is: 14929
Hope this can help someone

ES6 import nested function - mocha

I am using ES6, and I want to start testing using mocha & chai.
My current test file code is :
const assert = require('chai').assert;
var app = require('../../../../src/app/login/loginController').default;
describe('login Controller tests', function(){
it('no idea ', function(){
let result = app();
assert.equal(result, 'hello');
})
})
and my loginController.js is :
class LoginController {
checkout(){
return 'hello';
}
}
export default LoginController
I want to import the 'checkout' function into a variable inside my test file, but so far I am able to import only the class.
Will appreciate any help, thanks !
You cannot import methods directly from classes. If you want to import a function without a class as intermediary, then you need to define the function outside the class. Or if you really meant checkout to be an instance method, then you need to call it on an instance.
Here's an example file derived from yours:
export class LoginController {
// Satic function
static moo() {
return "I'm mooing";
}
// Instance method
checkout() {
return "hello";
}
}
// A standalone function.
export function something() {
return "This is something!";
}
And a test file that exercises all functions, adapted from the file you show in your question:
const assert = require('chai').assert;
// Short of using something to preprocess import statements during
// testing... use destructuring.
const { LoginController, something } = require('./loginController');
describe('login Controller tests', function(){
it('checkout', function(){
// It not make sense to call it without ``new``.
let result = new LoginController();
// You get an instance method from an instance.
assert.equal(result.checkout(), 'hello');
});
it('moo', function(){
// You get the static function from the class.
assert.equal(LoginController.moo(), 'I\'m mooing');
});
it('something', function(){
// Something is exported directly by the module
assert.equal(something(), 'This is something!');
});
});

Categories

Resources