Rewire private Typescript class methods - javascript

I'm trying to write unit tests for some private methods in my class using Typescript for my Node.js module.
I tried using rewire, but it's unable to access any methods (even public ones).
Here is my setup:
myclass.ts
class MyClass{
private somePrivateMethod() {
// do something
console.log(42);
}
public somePublicMethod() {
// do something
this.somePrivateMethod();
// do something
}
}
export default MyClass;
test.ts
import { expect } from 'chai';
import rewire from 'rewire';
describe('Test', function () {
describe('#somePrivateMethod()', function () {
const rewired = rewire('../src/myclass.ts');
//const rewired = rewire('../dist/myclass.js');
const method = rewired.__get__('somePrivateMethod');
});
});
I tried using rewire on the Typescript file and on the compiled JS file as well, but I get the same error:
ReferenceError: somePrivateMethod is not defined
I'm using Mocha with the following config:
'use strict';
module.exports = {
extension: ["ts"],
package: "./package.json",
reporter: "spec",
slow: 75,
timeout: 2000,
ui: "bdd",
"watch-files": ["test/**/*.ts"],
require: "ts-node/register",
};
Is there any solution for this problem?

I know is a pretty old question but if someone else find themself in the situation I've managed to find a solutions:
export class YourClass {
constructor( ) { }
private thisIsPrivate() {
//private stuff
}
otherPublicMethod() {
//public stuff
}
}
inside your test file you can import the class as usual to test public methods
import YourClass from '../src/classes/YourClass'
const testClass = new YourClass();
...
to test private methods import the class using rewire
import rewire from 'rewire'
const rewiredModule = rewire('../src/classes/YourClass')
const rewiredClass = rewiredModule.__get__('YourClass')
then inside your test
it('Test your private method', function () {
const myRewiredClassInstance = new rewiredClass()
myRewiredClassInstance.thisIsPrivate()
//do your expect here
}
The only downside is that the object returned is "any" and so no help from typescript.
you can even debug inside it if you have setup your debugger correctly
Enjoy everyone

Related

Jest Error: Expected '{', got 'namespace' ... Using namespace and #Injectable

I'm using: NestJS and I have a class exported using namespace. Right after the export namespace I have the NestJS #Injectable decorator and I get the following error when I try to run the test: × Expected '{', got 'namespace'
Without #Injectable the test runs without problems, but I need Injectable.
Class with Injectable
export namespace SearchCase {
#Injectable()
export class SearchCase {
constructor(private casesRepository: InterfaceRepository<Case.Case>) { }
async execute(request: RequestSearchCase): Promise<ResponseSearchCase> {
const searchResult = await this.casesRepository.search(request.search);
return {
searchResult,
};
}
}
}
Test
describe('Search Case', () => {
it('should be able return a case with substring', async () => {
const casesRepository = new InMemoryCaseRepository();
const searchCase = new SearchCase.SearchCase(casesRepository);
const createCase = new Case.Case({
utente: 'utente test',
caseOrigin: 'case origin test',
reportingDate: new Date(),
reporterName: 'Emanuela Xavier',
disease: 'disease test',
})
await casesRepository.create(createCase);
const response = await searchCase.execute({
search: 'Ema'
});
expect(response.searchResult.length).toBe(1);
expect(response.searchResult[0].reporterName).toContain('Ema');
});
});
ERROR
Error shown when I run the test
Removing #Injectable the test works without problem, but I need to use it.
I was using SWC/Jest instead of ts-jest, when I switched to ts-jest in my jest.config.ts the tests came back working even though I was using #Injectable.
I still don't understand why with #swc/jest it's failing, but for now it's working, when I have time I'll research more to find out the error.
Configuration with #swc/jest not working
Archive: jest.config.ts
"transform": {
"^.+\\.(t|j)s$": ["#swc/jest"]
}
Configuration with #SW/jest that work
Archive: jest.config.ts
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}

Unit tests to javascript classes (ES6) - Stubs not working

I have a problem with unit tests in a javascript (react) project.
Wanted to test the ApiClient class. For this I will have to isolate settingsApi, HttpClient and resources.
I using tools: mocha, chai, sinon
I also tried using rewire and I could not. I already read that it could be because of babel.
I've tried stubs like this:
sinon.stub(ApiClient.prototype, 'constructor');
Has anyone had this problem, can you give me a hand?
Below is part of the code:
File ApiClient.js
import settingsApi from '../../settings/api.json';
import HttpClient from './HttpClient';
import resources from './resources';
class ApiClient {
constructor() {
this.httpClient = new HttpClient(settingsApi);
}
get flux() {
return new resources.resourceOne(this.httpClient);
}
}
export default ApiClient;
FileHttpClient.js
class HttpClient {
/**
*
* #param {*} config
*/
constructor(config) {
this.basePath = `${config.protocol}://${config.host}:${config.port}`;
}
post(resource, options = {}, formatResponse) {
return this.request({
method: 'POST',
resource,
options,
formatResponse
});
}
export default HttpClient;
I could already solve my problem, I used the babel-plugin-rewire (https://github.com/speedskater/babel-plugin-rewire):
Installation:
yarn add babel-core babel-plugin-rewire --dev
Add to .babelrc
"plugins": [
"rewire"
]
Mock the dependencies example:
ApiClient.__Rewire__('settingsApi', {
paramConfig: 'config',
});
ApiClient.__Rewire__('HttpClient', class HttpClient {
constructor(config) {
}
});
ApiClient.__Rewire__('resources', {
resourceOne: class {
constructor(httpClient) {
}
});
Now instead of calling the dependencies calls the above functions.

Importing a Typescript Module that has a constructor

I want to break up my file into several modules. One module has a constructor. I am able to import my module into another file, but I don't know how call my constructor in the new file.
namespace CreditReports{
export class CreditReportVM {
//some code
constructor(targetElement: HTMLElement) {
ko.applyBindings(this, targetElement);
this.init();
}
public init = () => {
//some code
}
}
}
You just need to export the namespace too.
export namespace CreditReports {
//...
}
Then when you want to call the constructor:
import { CreditReports } from "./my-module";
//...
new CreditReports.CreditReportVM(myElement);
You should replace "./my-module" with the file name (an path too) where your typescript module is in.

prototype function missing when calling third party node package in Angular 2+

I am trying to use a third party Node module in an angular 2+ project.
installed with npm install --save ModuleName
The function in question is in a file named Foo.js and looks like this:
var foo = function(param1, param2) {
...
this.init();
}
foo.protoype = {
constructor: foo,
init: function(){
...
},
...
}
module.exports = foo;
index.js for the node module looks like:
var ModuleName = require("./src/ModuleName");
ModuleName.foo = require("./src/Foo");
module.exports = ModuleName;
I am trying to use the module in a Directive:
import { Directive, OnInit } from '#angular/core';
import { ModuleName } from "ModuleName"
#Directive({
selector: '[customDirective]'
})
export class CustomDirective implements OnInit {
constructor() {
...
}
ngOnInit() {
let poorlyNamedVariable = ModuleName.foo(param1, param2);
}
}
When foo is called it produces ERROR TypeError: this.init is not a function
console.log(this) in foo shows an instance of ModuleName which, in turn, has an instance of foo, which has a prototype where init is defined.
I suspect the problem stems from some sort of scoping issue, but am still too new to both Angular and Node to untangle the mess.
Use import * as ModuleName from "ModuleName" for CommonJS modules.

TypeScript call method in method = TypeError: this.print is not a function

I've got a main.ts file:
import { App } from './app';
import './styles.scss';
ready(new App().init);
function ready(fn) {
if (document.readyState !== 'loading'){
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}
and an app.ts file:
export class App {
constructor() {
}
private print = (str: string) => console.log(str);
init(){
this.print('test');
}
}
When I run this with the ts-loader in webpack using this tsconfig.json:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"sourceMap": true,
"lib": ["es5", "dom", "es2015.iterable"]
}
}
I receive the error: Uncaught TypeError: this.print is not a function
at HTMLDocument.App.init (app.ts:17)
I've tried creating the method as private print(str){ console.log(str); }
but this didn't solve it.
How can I get the method call in the init() method to work?
EDIT: forgot to add, I'm running webpack v. 1.14.0 and TypeScript 2.1.5 (tried with 2.1.4 also)
The problem is that you are passing the new App().init without binding it, and when it is executed the this isn't what you think it is.
You should do this:
let app = new App();
ready(app.init.bind(app));
Another option:
export class App {
constructor() {
this.init = this.init.bind(this);
}
private print = (str: string) => console.log(str);
init() {
this.print('test');
}
}
Or you can use an arrow function:
export class App {
constructor() {}
private print = (str: string) => console.log(str);
init = () => {
this.print('test');
}
}
The thing with arrow functions though is that it won't put the method on the prototype of the class, but instead will add it as a property of the instance.
That is fine in most cases, but if you're planning on subclassing the App class and override the init method then you'll have a problem calling super.init
The simplest solution is to write
import { App } from './app';
const app = new App();
ready(() => app.init());
This doesn't affect inheritance or prototypes or in any way change the behavior of class App.
That said, Nitzan Tomers's answer contains valuable information that is highly important to learn and understand.

Categories