У большинства из нас есть проблема с разъяснением Когда использовать неглубокий, а когда использовать крепление при тестировании с ферментом. В этом уроке я собираюсь обсудить различия между мелководьем и монтировкой, а также их плюсы и минусы.

Перед началом обучения я рекомендую вам иметь проект под названием «testing-demo-app», который я использовал для предыдущих руководств, и ссылка на него ниже.



wasuradananjith / testing-demo-app
Это исходный код для серии руководств« Тестирование с помощью Jest и Enzyme в React
github.com»



мелкий

  • Метод shallow используется для визуализации тестируемого нами компонента. Он не отображает дочерние компоненты.
  • В версии Enzyme менее 3 неглубокий метод не имеет возможности доступа к методам жизненного цикла. Но в Enzyme версии 3 у нас есть такая возможность.
  • Simple shallow вызывает методы constructor, render, componentDidMount (в Enzyme версии 3).
  • shallow + setProps вызывает componentWillReceiveProps, shouldComponentUpdate, componentWillUpdate, render, componentDidUpdate (в Enzyme версии 3) методы.
  • shallow + unmount вызывает метод componentWillUnmount.

устанавливать

  • Метод mount отображает полную модель DOM, включая дочерние компоненты родительского компонента, в котором мы запускаем тесты.
  • Это больше подходит, когда есть компоненты, которые напрямую мешают DOM API или методам жизненного цикла React.
  • Но это более затратно по времени выполнения.
  • Простое монтирование вызывает методы constructor, render, componentDidMount.
  • mount + setProps вызывает componentWillReceiveProps, shouldComponentUpdate, componentWillUpdate, render, componentDidUpdate.
  • mount + unmount вызывает метод componentWillUnmount.

Разница между мелкой и монтировкой на примере

Шаг 1. Создайте новый компонент с именем Form.js.

  • Откройте проект под названием «testing-demo-app». Создайте новый компонент с именем Form.js в папке testing-demo-app / src / components со следующим содержанием.
import React, { Component } from 'react';

class Form extends Component {

    constructor(props) {
        super(props);
        this.state = {
            firstNumber: '',
            secondNumber: '',
            componentState:'default'
        };

        this.handleFirstNumber = this.handleFirstNumber.bind(this);
        this.handleSecondNumber = this.handleSecondNumber.bind(this);
    }

    componentDidMount() {
        this.setState({ componentState: 'mounted' });
    }


    handleFirstNumber(event) {
        this.setState({ firstNumber: event.target.value });
    }

    handleSecondNumber(event) {
        this.setState({ secondNumber: event.target.value });
    }

    handleAdd(){
        const { firstNumber, secondNumber } = this.state;
        this.displayResult(parseInt(firstNumber) + parseInt(secondNumber))
    }

    handleSubtract(){
        const { firstNumber, secondNumber } = this.state;
        this.displayResult(parseInt(firstNumber) - parseInt(secondNumber))

    }

    displayResult(result){
        alert(result);
    }

    render() {
        const { firstNumber, secondNumber } = this.state;
        const { operator } = this.props;
        return (
            <form className='form-group'>
                <fieldset className='form-group'>
                    <label className='form-label'>
                        First Number:
                    </label>
                    <input type="text" id="number1" className='form-input' value={firstNumber} onChange={this.handleFirstNumber}/>
                </fieldset>
                <fieldset className='form-group'>
                    <label className='form-label'>
                        Second Number:
                    </label>
                    <input type="text" id="number2" className='form-input' value={secondNumber} onChange={this.handleSecondNumber}/>
                </fieldset>
                <div className='form-group'>
                    {operator === '+' &&
                    <button id='formButtonAdd' className='btn' type="button" onClick={() => this.handleAdd()}>Add</button>
                    }
                    {operator === '-' &&
                    <button id='formButtonSubtract' className='btn' type="button" onClick={() => this.handleSubtract()}>Subtract</button>
                    }
                </div>
            </form>
        );
    }

}

export default Form;
  • Перейдите в test-demo-app / src / App.css и очистите содержимое этого файла и добавьте следующие правила css.
html {
  box-sizing: border-box;
  font-size: 16px;
}

*,
*:after,
*:before {
  box-sizing: border-box;
}

body {
  font: 100% 'Roboto', arial, sans-serif;
  background: #f5f5f5;
}

form {
  padding: 2rem;
  margin-top: 2rem;
  margin-right: auto;
  margin-left: auto;
  max-width: 23.75rem;
  background-color: #fff;
  border-radius: 3px;
  box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1), 0 5px 15px rgba(0, 0, 0, 0.07);
}

h1 {
  margin-top: 2%;
  margin-bottom: 3.236rem;
  text-align: center;
  font-size: 1.618rem;
}

.form-group {
  padding: 0;
  border: 0;
}

.form-group + .form-group {
  margin-top: 1rem;
}

label {
  display: inline-block;
  margin-bottom: .5rem;
  font-size: .75rem;
  text-transform: uppercase;
  -ms-touch-action: manipulation;
  touch-action: manipulation;
}

input,
textarea {
  display: block;
  padding: .5rem .75rem;
  width: 100%;
  font-size: 1rem;
  line-height: 1.25;
  color: #55595c;
  background-color: #fff;
  background-image: none;
  background-clip: padding-box;
  border-top: 0;
  border-right: 0;
  border-bottom: 1px solid #eee;
  border-left: 0;
  border-radius: 3px;
  -webkit-transition: all 0.25s cubic-bezier(0.4, 0, 1, 1);
  transition: all 0.25s cubic-bezier(0.4, 0, 1, 1);
}

input:focus,
textarea:focus {
  outline: 0;
  border-bottom-color: #ffab00;
}

textarea {
  resize: vertical;
}

.btn {
  display: inline-block;
  padding: .75rem 1rem;
  width: 100%;
  margin-top: 1.618rem;
  font-weight: 400;
  text-align: center;
  text-transform: uppercase;
  color: #fff;
  vertical-align: middle;
  white-space: nowrap;
  background-color: #950aff;
  border: 1px solid transparent;
  box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1), 0 5px 15px rgba(0, 0, 0, 0.07);
  cursor: pointer;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  -webkit-transition: all 0.25s cubic-bezier(0.4, 0, 1, 1);
  transition: all 0.25s cubic-bezier(0.4, 0, 1, 1);
}

.btn:focus, .btn:hover {
  background-color: #c78eff;
  box-shadow: 0 18px 35px rgba(50, 50, 93, 0.1), 0 8px 15px rgba(0, 0, 0, 0.07);
}
.btn:focus {
  outline: 0;
}
  • Импортируйте файл App.css в файл testing-demo-app / src / App.js, добавив следующую строку в файл App.js.
import './App.css';

Шаг 2. Измените компонент Add.js

  • Перейдите в test-demo-app / src / components и добавьте следующий контент в файл Add.js.
import React, { Component } from 'react';
import Form from './Form';

class Add extends Component {
  render() {
    return (
        <div>
          <h1>Add Function</h1>
          <Form operator='+'/>
        </div>
    );
  }
}

export default Add;

Примечание - ‹h1› и ‹Form› являются дочерними компонентами ‹Add›

Теперь, когда вы запустите сервер, набрав npm start, вы должны получить интерфейс, как показано ниже.

Примечание. Введите 2 числа в поля ПЕРВЫЙ НОМЕР и ВТОРОЙ НОМЕР и нажмите кнопку ДОБАВИТЬ. Должно отображаться предупреждение JavaScript с результатом добавления введенных чисел.

Шаг 3 - Напишите и запустите тест для добавления компонента

  • Перейдите в test-demo-app / test и измените Add.test.js следующим содержанием.
import Add from '../src/components/Add';
import Form from '../src/components/Form';
let wrapper;
beforeEach(() => {
    wrapper = shallow(<Add />);
});
describe('<Add /> rendering', () => {
    it('should render one <h1>', () => {
        expect(wrapper.find('h1')).toHaveLength(1);
    });
it('should render one <Form>', () => {
        expect(wrapper.find(Form)).toHaveLength(1);
    });
it('should render 2 <label>s', () => {
        expect(wrapper.find('label')).toHaveLength(2);
    });
});
  • Откройте терминал в каталоге проекта и выполните приведенную ниже команду, чтобы запустить тесты в файле Add.test.js.
npm test Add.test.js
  • Теперь ваш тест должен завершиться неудачно, как показано ниже.

Почему это не удалось?

  • Вы можете видеть, что прошли 2 теста, которые проверяют рендеринг компонентов ‹h1› и ‹Form›.
  • Но тест для проверки рендеринга элементов ‹label› не удался.
  • ‹h1› и ‹Form› являются дочерними компонентами компонента ‹Add›. (это то, что мы сейчас тестируем)
  • Но элементы ‹label› являются дочерними компонентами ‹Form›.
  • Метод shallow не отображает элементы ‹label› внутри ‹Form›, поэтому этот тест не проходит.

Решение

  • Измените метод shallow в Add.test.js на mount и снова запустите тест.
beforeEach(() => {
    wrapper = mount(<Add />);
});
  • Теперь тест должен пройти следующим образом.

  • Причина этого в том, что при монтировании отображается полная модель DOM, включая дочерние компоненты в родительском компоненте. Итак, мы полностью отрисовали ‹Form› с ‹label›, которые мы ищем.

Что самое лучшее?

  • Рекомендуется как можно чаще использовать поверхностный, поскольку модульные тесты должны быть максимально изолированы.
  • Нам не нужно проверять блоки (компоненты) внутри блока (компонента) при запуске одного теста.
  • Когда вы используете mount, вы автоматически подвергаетесь логике всех модулей (компонентов) в вашем дереве рендеринга, что делает невозможным тестирование только рассматриваемого компонента.
  • При использовании mount это требует больших затрат времени на выполнение, поскольку для этого требуется JSDOM.

оказывать

  • Есть еще одна функция, например shallow и mount, которая называется визуализацией.
  • Это позволяет отображать статический HTML.
  • Оказывает детей.
  • Но у него нет доступа к методам жизненного цикла React.

Заключение

Теперь мы знаем плюсы и минусы использования методов shallow и mount, и я надеюсь, что вы сможете четко выбрать, какой из них лучше всего подходит для создаваемого вами теста.

Вы можете найти работу, которую мы проделали в этом руководстве, в ветке под названием tutorial-04 в репозитории git ниже.



В следующем руководстве я подробнее рассмотрю тестирование с помощью Jest и Enzyme, написав тесты для рендеринга, взаимодействий и вызовов методов жизненного цикла. А пока, до свидания!

Предыдущие уроки







📝 Прочтите этот рассказ позже в Журнале. Просыпайтесь каждое воскресное утро и слышите самые интересные истории, мнения и новости недели, ожидающие в вашем почтовом ящике: Получите примечательный информационный бюллетень›