ES6 import nested function - mocha - javascript

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!');
});
});

Related

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

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);
});
});

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);
});
});

Unit Testing ES6 Class with External Dependency

I'm trying to set a unit testing boilerplate for my company. Our front end projects are built with ES6 classes and have a dependency to our core product. The front end code gets wrapped through a build process in a whole other block of code that is basically a closure and captures the dependency. So we don't have to manually import it in order to use it.
Let's say the dependency is called productScope and it's an object that has some DOM models, internal APIs and parameters among many other things necessary for each project. At the moment, Mocha throws ReferenceError: productScope is not defined. How can I mock this object? Or should I just use the actual object?
Example:
class someClass {
constructor() {
const id = productScope.items[0].id
const item = productScope.domModel.querySelector('.some-div')
item.classList.add(`added-${id}`)
}
}
This get wrapped in core code like below:
(function(productScope) {
// front end code goes here
}(productScope)
Testing file:
import someClass from '../../js/someClass'
describe('someClass', function() {
const someClass = new someClass()
it('should be a class', function() {
console.log(someClass)
});
});
You can try something like this
describe('#someClass', () => {
let someClass;
beforeEach(() => {
global.productScope = {
// mocking productScope object
};
});
it('should be a class', () => {
someClass = new SomeClass;
console.log(someClass);
});
afterEach(() => {
delete global.productScope;
});
});
or alternatively if you want more specific mock logic for each test case
describe('#someClass', () => {
let someClass;
it('should be a class', () => {
global.productScope = {
// mocking productScope object
};
// Test logic start
someClass = new SomeClass;
console.log(someClass);
// Test logic end
delete global.productScope;
});
});
Looks like productScope is a global variable.
Something like this should work for you.
import someClass from '../../js/someClass';
describe('someClass', function() {
let someClass;
beforeEach(() => {
global.productScope = {
// you mock definition
someClass = new someClass();
};
});
it('should be a class', function() {
console.log(someClass)
});
});
I'm with other answers as well, as managing global variables seems to be the simplest and most straightforward solution.
However, you can use toString to get class's string representation, and eval it to bind to closure's scope:
class someClass {
constructor() {
this.id = scopedId
}
}
// pass class as an argument
function scopeFactory(classDef) {
// define scoped data
let scopedId = 2;
// eval is used to bind class to the local closure
// so `scopedId` will be in charge
return eval("(" + classDef + ")");
}
const scopedSomeClass = scopeFactory(someClass);
console.log(new scopedSomeClass)
Note that eval(someCLass.toString()) doesn't work without parentheses.
You can add it as a helper function, into your project.

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

Mocking named imports and constructors ES6 & Ava

I have a class constructor, with a function I want to stub:
class Service {
constructor(){}
async someFunction() {
try {
// does stuff
}
catch (e) {}
}
}
In the file I want to test, this is imported an used like so:
const { Service } = require('something')
const newService = new Service('xyz')
I'm struggling to get this to import & stub correctly in my tests.
Currently am importing like this:
t.context.service = {
Service: class Service {
constructor () {
this.someFunction = sinon.stub()
}
}
}
This import seems to work, but then I can't get a reference back to it through the constructed version. Any help on this one?
I want to be able to make an assertion like:
t.true(t.context.service.Service.someFunction.calledOnce)
AVA doesn't provide any stubbing. Have a look at https://github.com/testdouble/testdouble.js/ or http://sinonjs.org/.

Categories

Resources