How to spy on functions outside a component? - jestjs

// ./index.js
import { Component } from 'react';
export default class Test extends Component {
method () {
console.log('method()');
}
do () {
this.method();
func();
}
render () {
return null;
}
}
export function func () {
console.log('func()');
}
// ./index.test.js
import { shallow } from 'enzyme';
import React from 'react';
import * as Test from './index';
describe('<Test>', () => {
const component = shallow(<Test.default/>),
method_spy = jest.spyOn(component.instance(), 'method'),
func_spy = jest.spyOn(Test, 'func');
test('func()', () => {
component.instance().do();
expect(method_spy).toHaveBeenCalledTimes(1); // passed
expect(func_spy).toHaveBeenCalledTimes(1); // failed
});
});
I want to spy on function outside a component, but It doesn't work well.
I've got a message like Expected mock function to have been called one time, but it was called zero times.
And I don't want to use mock() method instead of spyOn() in the situation.
Is there way to fix it? Thaks you for reading. :D

It doesn't work because this line:
const func_spy = jest.spyOn(Test, 'func');
...is creating a spy on the module export for func...
...but Test.do doesn't call the module export for func, it calls func directly.
There are two options to fix it.
One is to move func into its own module.
Then the module export for it will be imported into index.js and called within Test.do...
...and when the module export for func is wrapped in a spy the spy will get called by Test.do.
The other option is to note that "ES6 modules support cyclic dependencies automatically" so a module can be imported into itself.
If the module is imported into itself then Test.do can call the module export for func:
import { Component } from 'react';
import * as index from './index'; // <= import the module into itself
export default class Test extends Component {
method() {
console.log('method()');
}
do() {
this.method();
index.func(); // <= use the module
}
render() {
return null;
}
}
export function func() {
console.log('func()');
}
...and the spy on the module export for func will be called as expected:
import { shallow } from 'enzyme';
import React from 'react';
import * as Test from './index';
describe('<Test>', () => {
const component = shallow(<Test.default />),
method_spy = jest.spyOn(component.instance(), 'method'),
func_spy = jest.spyOn(Test, 'func');
test('func()', () => {
component.instance().do();
expect(method_spy).toHaveBeenCalledTimes(1); // Success!
expect(func_spy).toHaveBeenCalledTimes(1); // Success!
});
});

Related

TypeError: class is not a constructor (TypeScript)

I'm creating an Node.JS API, using Typescript v4.9.4, and Module Alias v2.2.2
There is a factory that creates the controller SignUp like this:
import { SignUpController } from '#/presentation/controllers'
import { type Controller } from '#/presentation/protocols'
import { makeDbAuthentication, makeDbAddUser } from '#/main/factories/usecases'
import { makeSignUpValidator } from './make-sign-up-validator-factory'
export const makeSignUpController = (): Controller => {
const controller = new SignUpController(makeDbAddUser(), makeSignUpValidator(), makeDbAuthentication())
return controller
}
I have a problem on the makeDbAddUser() that has this code:
import { DbAddUser } from '#/data/usecases'
import { type AddUser } from '#/domain/usecases'
import { UserMongoRepository } from '#/infra/db/mongodb/user-mongo-repository'
import { BcryptAdapter } from '#/infra/cryptography'
export const makeDbAddUser = (): AddUser => {
const salt = 12
const bcryptAdapter = new BcryptAdapter(salt)
const userMongoRepository = new UserMongoRepository()
return new DbAddUser(bcryptAdapter, userMongoRepository, userMongoRepository)
}
The error occurs on the line where new UserMongoRepository() is created.
const userMongoRepository = new db_1.UserMongoRepository();
^
TypeError: db_1.UserMongoRepository is not a constructor
And here is the UserMongoRepository class:
export class UserMongoRepository implements AddUserRepository, LoadUserByEmailRepository, CheckUserByEmailRepository, UpdateAccessTokenRepository {
// eslint-disable-next-line #typescript-eslint/no-useless-constructor
constructor () {}
async add (data: AddUserRepository.Params): Promise<AddUserRepository.Result> {
//code...
}
// other methods
}
To me everything seems fine, I have other classes and factories that I use in the same way. I'm probably missing something on the impor/export maybe? But I dont really know where to start looking anymore.
I already tried adding a constructor, even empty on my Class, but the error persists.
Also, tried the solutions on this thread, about a similar problem. Putting the export { UserMongoRepository } in the end of the file.
As I'm using ModuleAlias to have better import names, I tried without # like so:
import { UserMongoRepository } from '../../../infra/db/mongodb/user-mongo-repository'
But the problem persists.

Cypress ReferenceError: input is undefined (Cypress, Cucumber, POM)

I'm struggling to find the reason for this:
I'm using Cypress, Cucumber and POM approach.
This is my POM file LoginPage.js
class LoginPage {
get usernameInput() {
return cy.get('input[name="email"]');
}
get passwordInput() {
return cy.get('input[name="password"]');
}
get submitBT() {
return cy.get('button[type="submit"]');
}
loginToCMS() {
cy.visit('https://example.com')
usernameInput.type('admin#admin.com');
}
}
export default new LoginPage
Then I try to call loginToCMS() function in another loginSteps.js file:
import { Given, When, Then, And } from 'cypress-cucumber-preprocessor/steps'
import LoginPage from '../../pages/LoginPage'
Given('user is logged in CMS', () => {
LoginPage.loginToCMS();
})
When run feature file, I get an error: Reference Error: usernameInput is not defined
usernameInput is a function on the LoginPage class, so you have to use this to call it.
class LoginPage {
get usernameInput() {
return cy.get('input[name="email"]');
}
...
loginToCMS() {
cy.visit('https://example.com');
this.usernameInput.type('admin#admin.com');
}
}

tests fail when injecting a class with decorator

This is likely an environment issue on my end, but I'm having problems testing injected classes that use decorators.
Am hoping someone can see what I'm missing.
Note: that when I run the code, it runs fine. The error only occurs when running jest tests. I have a full test suite that works fine. The test in question that fails passes when I remove the decorator annotation and no longer have the class extend EventEmitter.
I also use ts-node to run the code. I use ts-node ./node_modules/.bin/jest src/event-emitter-inversify/__tests__/event-emitter-inversify.test.ts
I'm not sure why the tests fail and the run succeeds.
I consistently see the error TypeError: Cannot read property 'constructor' of null in my tests.
Here is the setup.
event-emitter-inversify.ts
import 'reflect-metadata';
import { EventEmitter } from 'events';
import { ContainerModule, decorate, injectable, interfaces } from 'inversify';
decorate(injectable(), EventEmitter);
#injectable()
export class EventEmitterChild extends EventEmitter {
constructor() {
super();
}
greet() {
console.log('hello world.');
}
}
export const EventEmitterModule = new ContainerModule(
(bind: interfaces.Bind) => {
bind<EventEmitterChild>('EventEmitterChild').to(EventEmitterChild).inSingletonScope();
}
);
event-emitter-inversify-dependent.ts
import { inject, injectable } from 'inversify';
import { EventEmitterChild } from './event-emitter-inversify';
#injectable()
export class EventEmitterDependent {
constructor(#inject('EventEmitterChild') private readonly _eventEmitterChild: EventEmitterChild) {
}
run() {
this._eventEmitterChild.greet();
}
}
run.ts (this runs fine!)
import { Container } from 'inversify';
import { EventEmitterChild, EventEmitterModule } from './event-emiiter-inversify';
import { EventEmitterDependent } from './event-emitter-inversify-dependent';
const referenceContainer = new Container();
referenceContainer.load(EventEmitterModule);
const eventEmitterChild = referenceContainer.get<EventEmitterChild>('EventEmitterChild');
eventEmitterChild.greet();
const eventEmitterDependent = referenceContainer.get<EventEmitterDependent>('EventEmitterDependent');
eventEmitterDependent.run();
event-emitter-inversify.test.ts (this fails)
import { Container } from 'inversify';
import { EventEmitterChild, EventEmitterModule } from '../event-emitter-inversify';
describe('EventEmitter test', () => {
const container: Container = new Container();
beforeAll(async () => {
container.load(EventEmitterModule);
});
test('get EventEmitterChild from container', () => {
const eventEmitterChild = container.get<EventEmitterChild>('EventEmitterChild');
expect(eventEmitterChild).toBeDefined();
});
});
I run the test with:
ts-node ./node_modules/.bin/jest src/event-emitter-inversify/__tests__/event-emitter-inversify.test.ts
And it fails with:
TypeError: Cannot read property 'constructor' of null
stacktrace
at getClassPropsAsTargets (node_modules/inversify/lib/planning/reflection_utils.js:82:75)
at getClassPropsAsTargets (node_modules/inversify/lib/planning/reflection_utils.js:84:27)
at getClassPropsAsTargets (node_modules/inversify/lib/planning/reflection_utils.js:84:27)
at getTargets (node_modules/inversify/lib/planning/reflection_utils.js:28:27)
at Object.getDependencies (node_modules/inversify/lib/planning/reflection_utils.js:12:19)
at node_modules/inversify/lib/planning/planner.js:106:51
at Array.forEach (<anonymous>)
at _createSubRequests (node_modules/inversify/lib/planning/planner.js:94:20)
at Object.plan (node_modules/inversify/lib/planning/planner.js:136:9)
at node_modules/inversify/lib/container/container.js:318:37
Any ideas of what I'm missing or doing wrong?

In react, I define a function but it's appear undefine

I defined a function, but it's undefined in the reference precess. I tried to add breakpoints, the results show that it is a function, but continues to perform an error again, you mean form the callback?
That's the part I quoted, prompt requestServer is not the function, but it has been defined in the code below, the reason why I don't know whether the callback
import React, {Component, PropTypes} from 'react';
import GLogin from './Login';
var serverMethon = require('../../server/requestServer');
export default class LoginContainer extends Component {
constructor(props) {
super(props);
this.state = {
}
}
getLogin = (value) => {
const {selectView} = this.props;
const requestServer = serverMethon.requestServer;
requestServer('login', value, function(t) {
const data = JSON.parse(t.text);
if (data.state != "successful") {
alert("Login fail!")
return;
}
selectView('SearchContainer');
})();
}
render() {
return (
<GLogin
getLogin={this.getLogin}
{...this.props}/>
)
}
}
Function definitions section
var superagent = require('superagent');
export const requestServer = (position, info, callback) => {
superagent.post(`http://localhost:3000/${position}`)
.send(info)
.end((error, doc)=>{
if(error){
throw error
}
callback(doc)
})
}
// proper way
class App extends React.Component{
constructor (props){
super(props);
this.sampleMethod = this.sampleMethod.bind(this);
}
sampleMethod(){
console.log('Sample method called');
}
render(){
return <button onClick={this.sampleMethod}>Click Me</buttom>
}
}
Your mistake is you called your method as this.getLogin. But you don't bind the method. So you can't use this method with this.getLogin. You can use getLogin method without binding. This time you called the method by getLogin but you get context inside getLogin method the context is not be your component. At the time your context is window so you get window object.
So first bind your method then use it.
// your mistake
getLogin = (value)=>{
// your logic
}
// proper way
getLogin(value){
// your logic
}

Get path of required module in NodeJS

I have 2 classes, one that extends from the other. I need to have a method that can be called from extending classes but resolves the path to reflect whatever directory the extending class module is in.
/main.js
class Main {
getTemplate () {
return readFileSync(__dirname + './template.ejs')
}
}
/some/path/module.js (also contains /some/path/template.ejs)
class Module extends Main {
}
In main code
const m = new Module()
m.getTemplate() // /some/path/template.ejs
The above doesn't work since __dirname doesn't reflect the 'module.js' path.
I've played around with require.resolve but I am not sure how I can use this from within the module.
/main.js
class Main {
constructor(dirname) {
this.dirname = dirname;
}
getTemplate () {
return readFileSync(this.dirname + './template.ejs')
}
}
/some/path/module.js (also contains /some/path/template.ejs)
class Module extends Main {
constructor() {
super(__dirname);
}
}
Ok. I've got a working alas hokey solution.
class Main {
constructor () {
try {
throw new Error()
} catch(e) {
const m = e.stack.match(/at new \w+ \(([^:]+)/)
this.dirname = path.dirname(m[1])
}
}
getTemplate () {
return readFileSync(this.dirname + '/template.ejs')
}
}
class Module extends Main {
}

Resources