У меня есть функция HOC, которая получает компонент React и возвращает этот компонент реакции с двумя новыми свойствами метода (handleBack и moveitOnTop), например:
import React, { Component } from "react";
import classNames from "classnames";
export default WrappedComponent => {
return class extends Component {
constructor(props) {
super(props);
this.moveitOnTop = this.moveitOnTop.bind(this);
this.handleBack = this.handleBack.bind(this);
this.state = {
isSearchActive: false
};
}
moveitOnTop(flag) {
this.setState({ isSearchActive: flag });
window.scrollTo(0, -100);
}
handleBack() {
this.setState({ isSearchActive: false });
if (document.body.classList.contains("lock-position")) {
document.body.classList.remove("lock-position");
}
}
render() {
const props = {
...this.props,
isSearchActive: this.state.isSearchActive,
moveitOnTop: this.moveitOnTop,
goBack: this.handleBack
};
const classes = classNames({ "c-ftsOnTop": this.state.isSearchActive });
return (
<div className = {classes}>
<WrappedComponent {...props} />
</div>
);
}
};
};
Компонент:
//import fireAnalytics
import { fireAnalytics } from "@modules/js-utils/lib";
class MyComponent extender Component{
constructor(){
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
// calling analytics function by passing vals
fireAnalytics({
event: "GAEvent",
category: "",
action: `Clicked on the ${e.target.id} input`,
label: "Click"
});
// I CALL THE HOC PROPERTY
this.props.moveitOnTop(true);
// I CALL THE HOC PROPERTY
this.props.handleBack();
}
render(){
return(
<div className = "emailBlock">
<input type = "text" onClick = {handleClick} />
<Button className = "submit">Submit</Button>
</div>
)
}
}
// export HOC component
export default hoc(MyComponent);
// export just MyComponent
export {MyComponent};
Я хочу протестировать HOC:
.c-ftsOnTop существуетonClick функцию, которая вызывает this.props.handleBack и `this.props.moveitOnTop'emailBlock.Тест, который я пробовал, но терпит неудачу:
import { mount, shallow } from 'enzyme';
import sinon from 'sinon';
import React from 'react';
import { expect } from 'chai';
import hoc from '....';
import {MyComponent} from '...';
import MyComponent from '....';
it('renders component', () => {
const props = {}
const HocComponent = hoc(MyComponent);
const wrapper = mount(
<HocComponent {...props} />
);
console.info('wrapper:', wrapper);
expect(wrapper.find('.c-ftsOnTop')).to.have.lengthOf(1);
expect(wrapper.hasClass('c-fts-input-container')).toEqual(true);
})
Ошибка
AssertionError: expected {} to have a length of 1 but got 0
console.info: wrapper: ReactWrapper {}
Может ли кто-нибудь помочь мне в том, как сделать HOC?
Можете ли вы использовать макеты для ваших компонентов ввода и вывода?
Я думаю, что такое тестирование (деталей реализации) не следует поощрять, и вместо этого вы должны проверить, что волнует пользователя вашего компонента, например, наличие элемента при передаче правильных реквизитов и т. д. Я предлагаю вам взять посмотрите на react-testing-library от Kent C Dodds и ознакомьтесь с примерами для тестирования HOC, это намного проще и эффективнее или даже лучше.





Вот рабочий тест:
import { mount } from 'enzyme';
import React from 'react';
import WrappedMyComponent from './MyComponent';
it('renders component', () => {
const props = {}
const moveitOnTopSpy = jest.spyOn(WrappedMyComponent.prototype, 'moveitOnTop');
const handleBackSpy = jest.spyOn(WrappedMyComponent.prototype, 'handleBack');
const wrapper = mount(
<WrappedMyComponent {...props} />
);
// 1. I need to check that class .c-ftsOnTop exists
wrapper.setState({ isSearchActive: true }); // <= set isSearchActive to true so .c-ftsOnTop is added
expect(wrapper.find('.c-ftsOnTop')).toHaveLength(1); // Success!
// 2. I need to check onClick function that calls this.props.handleBack & `this.props.moveitOnTop'
window.scrollTo = jest.fn(); // mock window.scrollTo
wrapper.find('input').props().onClick();
expect(moveitOnTopSpy).toHaveBeenCalled(); // Success!
expect(window.scrollTo).toHaveBeenCalledWith(0, -100); // Success!
expect(handleBackSpy).toHaveBeenCalled(); // Success!
// 3. I need to check if className emailBlock exists
expect(wrapper.find('.emailBlock')).toHaveLength(1); // Success!
})
Подробности
.c-ftsOnTop добавляется только тогда, когда isSearchActive является true, поэтому просто установите состояние компонента, чтобы добавить класс.
Если вы создадите своих шпионов на методах прототипа для moveitOnTop и handleBack, то когда hoc создаст свои методы экземпляра, привязав их к this в конструкторе, методы экземпляра будут привязаны к вашим шпионам.
window.scrollTo регистрирует ошибку на консоли по умолчанию в jsdom, поэтому вы можете смоделировать ее, чтобы избежать этого сообщения об ошибке и убедиться, что она была вызвана с ожидаемыми аргументами.
Обратите внимание, что приведенный выше тест требует исправления следующих опечаток в MyComponent:
extender должно быть extendsconstructor должен принять props аргументonClick должен быть привязан к this.handleClick, а не просто handleClickhandleClick следует называть this.props.goBack() вместо this.props.handleBack()(Я предполагаю, что MyComponent был просто собран как пример фактического компонента)
да спасибо. Я забыл упомянуть, что внутри handleClick есть и другие вещи, например, вызов другой функции перед вызовом moveitOnTop, например fireAnalytics(a,b,c,d) . Так что я могу издеваться над этой функцией, как const fireAnalytics = jest.fn();. ?
Является ли fireAnalytics функцией, которая импортируется в ваш компонент? Если это так, то да, вы можете смоделировать его из своего тестового файла. @ТеоИцарис
На самом деле это модуль, и я использую функцию. Я обновил код, я вызываю fireAnalytics в handleClick. Если я просто издеваюсь над ним в тесте, я думаю, этого недостаточно, мне также нужно как-то внедрить его в компонент?
Зачем добавлять класс? Свойство состояния, которое переключает его по умолчанию, имеет значение false, верно? Вы смотрели на методы отладки или html, чтобы увидеть, что отображается?