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

JSX для непосвященных очень похож на ваш обычный HTML, за исключением того, что вы часто можете встретить выражения JavaScript, такие как переменные, вызовы функций или троичные, разбросанные по всему тексту. Это большая часть того, что дает React его мощь, его способность использовать JSX как средство более простого использования JavaScript при создании контента или предоставлении пользователям быстрого и легкого взаимодействия с вашим приложением.

Один из наиболее распространенных способов использования React - сопоставление массивов, переданных в качестве свойств, для быстрого создания коллекции из нескольких небольших фрагментов контента с помощью одного шаблона. Обычно это выполняется с помощью метода, который вызывается в вашем JSX следующим образом:

import React, { Component} from 'react';
class List extends Component {
  generateListItems = () => {
    return this.props.list.map((item, index) => {
      return <li className='list-item' key={index}>{item}</li>
    })
  }
  render() {
    return(
      <div>
        <ul>
          {this.generateListItems()}
        </ul>  
      </div>
    )  
  }
}

Если вы хотите правильно протестировать этот компонент, вы не можете просто остановиться на снимке. У вас есть метод, который что-то возвращает и требует проверки, но как вы оцениваете массив JSX? Обычные попытки взглянуть на что-то вроде того, что возвращается из массива из двух элементов списка, дадут вам что-то вроде этого:

[ { '$$typeof': Symbol(react.element),
    type: 'li',
    key: '0',
    ref: null,
    props:
    { className: 'list-item',
    children: 'First list item' },
    _owner: null,
    _store: {} },
  { '$$typeof': Symbol(react.element),
    type: 'li',
    key: '1',
    ref: null,
    props:
    { className: 'list-item',
      children: 'Second list item' },
    _owner: null,
    _store: {} 
} ]

Это практически невозможно протестировать как есть, поэтому нам нужно другое решение. Поскольку весь смысл JSX заключается в том, чтобы позволить нам в конечном итоге иметь некоторый HTML, который будет отображаться в DOM, это, в конечном счете, то, на что мы действительно заинтересованы. Итак, как нам превратить вышеприведенное в простую строку HTML, которую можно оценить с помощью нашего набора для тестирования?

Во-первых, мы хотим использовать метод shallow() от Enzyme так же, как мы имитируем рендеринг наших компонентов с помощью обычного модульного тестирования:

it("should return some jsx", () => {
  const result = wrapper
    .instance()
    .generateListItems()
    .map(point => {
      return shallow(point);
    });
  console.log(result);
});

Использование map() позволит нам перебирать каждый возвращаемый объект и правильно вызывать для него shallow(). Без этого map() вы попытаетесь вызвать shallow() в массиве, и эта собака не будет охотиться.

Приведенный выше тест выведет на консоль следующее:

[ ShallowWrapper {}, ShallowWrapper {} ]

Так что это не то, что мы можем оценивать для тестирования отдельно, пока мы не углубимся в документацию по Enzyme и не посмотрим, какие методы shallow() предоставляет нам. Единственный метод, который нас особенно интересует, - это html().

Этот метод возвращает строку визуализированной HTML-разметки всего текущего дерева визуализации, что делает его удобным не только для тестирования вышеупомянутого метода generateListItems(), но и для методов тестирования, которые возвращают массив дочерних компонентов React.

Если мы изменим наш вышеупомянутый тест, чтобы использовать метод html():

it("should return some jsx", () => {
const result = wrapper
    .instance()
    .generateListItems()
    .map(point => {
      return shallow(point).html();
    });
console.log(result);
});

… Вместо этого мы запишем в журнал что-то вроде этого:

[ 
  '<li className="list-item">\n First list item \n</li>', 
  '<li className="list-item">\n Second list item \n</li>'
]

Теперь мы в деле! Теперь мы действительно можем предвидеть, что вернет shallow().html(), и читать это как человек, а это значит, что мы можем протестировать это соответствующим образом!

it("should return some jsx", () => {
  const expected = [ 
    '<li className="list-item">\n First list item \n</li>', 
    '<li className="list-item">\n Second list item \n</li>'
  ]
  const result = wrapper
    .instance()
    .generateListItems()
    .map(point => {
      return shallow(point).html();
    });
  expect(result).toEqual(expected);
});

Вот и все, как вы тестируете то, что возвращает JSX!