Redux: Невозможно ссылаться на контейнеры, завернутые в Provider, или подключиться к Enzyme.

Созданный на 20 мар. 2016  ·  51Комментарии  ·  Источник: reduxjs/redux

Кажется, я не могу сослаться на что-либо, завернутое в <Provider> и connect

// test
let component = shallow(<Provider store={store}><ContainerComponent /></Provider>);
component.find('#abc'); // returns null

let component = shallow(<Provider store={store}><div id="abc"></div></Provider>);
component.find('#abc'); // returns the div node

// ContainerComponent
const Component = ({...}) => (<div id="abc"></div>);
export default connect(..., ...)(Component);

Я следовал: https://github.com/reactjs/redux/issues/1481 к примерам, где тесты были написаны с помощью фермента, однако контейнеры в них никогда не тестировались. Так что я не знаю, должен ли я тестировать смарт-контейнеры?

@fshowalter Есть идеи?

Самый полезный комментарий

Хотя это не имеет прямого отношения к Redux. Я решил это, снова вызвав shallow() в контейнере. Он отображает внутренний компонент с переданным ему состоянием хранилища.

Пример:

import React from 'react';
import {expect} from 'chai';
import {shallow} from 'enzyme';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import events from 'events';

const mockStore = configureMockStore([ thunk ]);
const storeStateMock = {
  myReducer:{
    someState: 'ABC'
  }
};

let store;
let component;
describe('tests for MyContainerComponent', () => {
  beforeEach(() => {
    store = mockStore(storeStateMock);
    component = shallow(<MyContainerComponent store={store} />).shallow();
  });

  it('renders container', () => {
    expect(component.find('div.somediv')).to.have.length.of(1);
  });
  ...
});

Надеюсь это поможет

Все 51 Комментарий

Ни connect() , ни Provider не являются частью этой библиотеки. Обсуждать и отслеживать такие проблемы легче, когда они регистрируются в соответствующем репозитории: https://github.com/reactjs/react-redux.

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

Здесь две мысли:

Во-первых, обратите внимание, что компонент-оболочка, сгенерированный connect() , на самом деле ищет props.store , прежде чем он ищет context.store , поэтому, если вы действительно чувствуете, что вам нужно протестировать подключенный компонент , вы должны иметь возможность отображать <ConnectedComponent store={myTestStore} /> , не беспокоясь о провайдере, контексте или чем-то еще.

Второй вопрос заключается в том, действительно ли вам нужно беспокоиться о тестировании полносвязного компонента. Аргумент, который я видел, заключается в том, что если вы можете протестировать свой «простой» компонент с определенными реквизитами, и вы можете протестировать свою реализацию mapStateToProps , вы можете с уверенностью предположить, что react-redux правильно соберет их вместе, и не нужно на самом деле тестировать подключенную версию.

@gaearon ты прав, извини. Не знал, поднять ли это в репозитории реакции или фермента.

@markerikson Причина тестирования смарт-компонента - mapToDispatchProps , где я хотел убедиться, что обернутый компонент вызывает правильный диспетчер. Простая передача хранилища в ConnectedComponent означает, что я не буду тестировать сопоставление состояний или диспетчерские функции обратного вызова.

Я подумаю об этом подробнее и подниму вопрос в соответствующем репо, если мне это понадобится. Спасибо за помощь.

@mordra : когда вы говорите, что «был вызван правильный диспетчер», вы на самом деле имеете в виду «создатель правильного действия»?

Вы можете сделать что-то вроде этого (синтаксис, вероятно, отключен, но вы должны понять идею):

let actionSpy = sinon.spy();

let wrapper = shallow(<PlainComponent someActionProp={actionSpy} />);
wrapper.find(".triggerActionButton").simulate("click");
expect(actionSpy.calledOnce).to.be.true;
expect(actionSpy.calledWith({type : ACTION_I_WAS_EXPECTING)).to.be.true;

Другими словами, визуализируйте свой обычный компонент, передайте шпионам реквизиты, которые были бы действиями, возвращенными из mapDispatch , и запустите любое поведение, необходимое компоненту, чтобы заставить его вызывать эти действия.

Кроме того, согласно моему предыдущему комментарию: вы должны иметь возможность протестировать свои mapStateToProps и mapDispatchToProps отдельности и быть уверенными, что React-Redux вызовет их соответствующим образом, без необходимости пытаться протестировать подключенную версию. себя, чтобы убедиться, что это так.

@markerikson Я не могу сделать то, что вы предлагаете, потому что мой PlainComponent не знает об действиях или создателях действий. Я не знаю, правильный ли это способ сделать это, но если вы посмотрите на:
https://github.com/mordra/cotwmtor/blob/master/client/charCreation/charCreation.jsx
вы увидите, что мой mapDispatchToProps содержит всю логику:

const mapDispatchToProps = (dispatch) => {
  return {
    onCompleted      : (player) => {
      Meteor.call('newGame', player, function (data) {
        console.log('new game return: ' + data);
      });
      dispatch(routeActions.push('/game'));
      dispatch({type: "INIT_GAME", map: generateAreas(), buildings: generateBuildings(dispatch)});
    },
    onCancelled      : () => dispatch(routeActions.push('/')),
    onChangeName     : input => dispatch(actions.changeName(input)),
    onChangeGender   : gender => dispatch(actions.setGender(gender)),
    onSetDifficulty  : lvl => dispatch(actions.setDifficulty(lvl)),
    onChangeAttribute: (attr, val) => dispatch(actions.setAttribute(attr, val))
  }
};

поэтому, если бы я следил за реквизитами, переданными в PlainComponent, я не смог бы проверить, вызываются ли правильные действия, а скорее правильность параметров, переданных создателям действий.
Затем мне нужно будет протестировать компонент connect отдельно.

Ах. Это помогает мне понять больше.

Итак, с оговоркой, что у меня на самом деле очень мало практического опыта написания тестов, пара мыслей:

  • На самом деле у вас есть создатели действий, они просто не определены отдельно, потому что вам нужен доступ к dispatch . Это не очень легко проверить само по себе. Вы, вероятно, захотите также проверить их поведение, и для этого вы захотите определить их как свои собственные функции вне mapDispatch .
  • Все они выглядят как очень хорошие кандидаты для использования с redux-thunk, что, безусловно, упростит определение этих функций самостоятельно и позволит им получить доступ к dispatch .
  • Ваш PlainComponent _знает_ о "действиях" или, по крайней мере, обратных вызовах в этом случае, поскольку вы передаете их, и где-то в вашем компоненте вы запускаете this.props.onSetDifficulty("HARD") или что-то в этом роде. Когда я предлагал использовать шпионов вместо действий, я предлагал заменить именно это. Таким образом, если вы передали шпион за onSetDifficulty , вы можете убедиться, что ваш компонент вызвал его и передал приемлемое значение для уровня сложности.

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

Кроме того, с точки зрения компонента: в конечном счете, он не беспокоится о том, был ли отправлен {action: CHANGE_NAME, name : "Fred"} . Все, что он знает, это то, что он вызвал this.props.onChangeName("Fred") , и _это_ то, что вы должны попытаться проверить, - что он правильно вызвал обратный вызов свойства.

@mordra в вашем случае я бы экспортировал mapDispatchToProps и протестировал его изолированно. Вы можете проверить правильность имен свойств и передать шпион для отправки, чтобы проверить своих создателей действий.

Тем не менее, я склонен избегать mapDispatchToProps в пользу mapActionCreatorsToProps , который отображает уже сформированные создатели действий, которые я могу легко протестировать изолированно. В этом случае я просто проверяю, что все мои реквизиты определены для защиты от опечаток при импорте.

Наконец, обратите внимание, что вы можете использовать обычный (не поверхностный) рендеринг. Для этого вам понадобится либо jsdom, либо настоящий браузер, но тогда вы сможете рендерить любой уровень глубины.

Не знал, поднять ли это в репозитории реакции или фермента

Извините, я не имел в виду репозитории React или Enzyme. Я имел в виду React Redux , библиотеку, которую вы используете.

Спасибо за помощь, ребята, это много информации для меня, чтобы пойти и переварить. @markerikson @fshowalter Я еще немного осмотрелся и приму ваше предложение экспортировать mapState/Dispatch и протестировать их отдельно, имеет смысл протестировать обратные вызовы, которые вызывают ожидаемые действия.

Компонент @gaearon без сохранения состояния не отображается без поверхностного и простого обхода проблем, похоже, возникают проблемы с отображением компонентов с состоянием, которые имеют вложенные компоненты без состояния, поэтому я решил пока избегать этого пути.

Я не уверен, о каких проблемах вы говорите. Вы можете использовать поверхностный рендеринг с функциональными компонентами. Поверхностный рендеринг работает только на один уровень в глубину (независимо от того, как определен компонент). Это его главная особенность — он не повторяется, поэтому тесты компонентов остаются независимыми. Если у вас есть конкретные проблемы с поверхностным рендерингом, которые вы можете воспроизвести, сообщите об ошибках в репозиторий React. Спасибо!

@gaearon : чтобы уточнить, если я сделаю это:

let wrapper = shallow(<A><B /></A>);

будет ли B отображаться или нет? Потому что я думаю, что исходный вопрос заключался в том, чтобы попытаться отобразить <Provider> вокруг подключенного компонента, чтобы проверить соединение.

Хотя это не имеет прямого отношения к Redux. Я решил это, снова вызвав shallow() в контейнере. Он отображает внутренний компонент с переданным ему состоянием хранилища.

Пример:

import React from 'react';
import {expect} from 'chai';
import {shallow} from 'enzyme';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import events from 'events';

const mockStore = configureMockStore([ thunk ]);
const storeStateMock = {
  myReducer:{
    someState: 'ABC'
  }
};

let store;
let component;
describe('tests for MyContainerComponent', () => {
  beforeEach(() => {
    store = mockStore(storeStateMock);
    component = shallow(<MyContainerComponent store={store} />).shallow();
  });

  it('renders container', () => {
    expect(component.find('div.somediv')).to.have.length.of(1);
  });
  ...
});

Надеюсь это поможет

Должны ли мы сделать более доступный способ сделать это с помощью пользовательского модуля?

Редактировать: Таким образом, мы можем объявить соединение как один метод для Enzyme, например, component = shallowWithConnect()

@ev1stensberg Это отличная идея. Решили ли мы, является ли это подходом, и/или работа над этим началась? Если нет, я хотел бы внести свой вклад.

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

// test
let component = shallow(<Provider store={store}><ContainerComponent /></Provider>);
component.find('#abc'); // returns null

let component = shallow(<Provider store={store}><div id="abc"></div></Provider>);
component.find('#abc'); // returns the div node

// ContainerComponent
export const Component = ({...}) => (<div id="abc"></div>); // I EXPORT THIS, TOO, JUST FOR TESTING
export default connect(..., ...)(Component);

Тогда просто сделай

const component = shallow(<Component {...props} />)
// test component

И, как указывали выше другие, если у вас также есть покрытие для функций, которые вы передаете в соединение, вы должны иметь полное покрытие для вашего компонента в целом.

Ну, первое, на что следует обратить внимание, это философия :

  1. Сообщить о состоянии из хранилища в
  2. Измените состояние с помощью диспетчерских действий на основе Мероприятия.

Если я в порядке, вам нужно проверить только 2 вещи:

  1. ваш компонент получает правильные реквизиты, сгенерированные из состояния (может быть, через селекторы)?
  2. Ваш компонент отправляет правильные действия?

Итак, это мой подход

import React from 'react';
import { shallow } from 'enzyme';
import { fromJS } from 'immutable';
import { createStore } from 'redux';
// this is the <Map /> container
import Map from '../index';
// this is an action handled by a reducer
import { mSetRegion } from '../reducer';
// this is an action handled by a saga
import { sNewChan } from '../sagas';
// this is the component wrapped by the <Map /> container
import MapComponent from '../../../components/Map';

const region = {
  latitude: 20.1449858,
  longitude: -16.8884463,
  latitudeDelta: 0.0222,
  longitudeDelta: 0.0321,
};
const otherRegion = {
  latitude: 21.1449858,
  longitude: -12.8884463,
  latitudeDelta: 1.0222,
  longitudeDelta: 2.0321,
};
const coordinate = {
  latitude: 31.788,
  longitude: -102.43,
};
const marker1 = {
  coordinate,
};
const markers = [marker1];
const mapState = fromJS({
  region,
  markers,
});
const initialState = {
  map: mapState,
};
// we are not testing the reducer, so it's ok to have a do-nothing reducer
const reducer = state => state;
// just a mock
const dispatch = jest.fn();
// this is how i mock the dispatch method
const store = {
  ...createStore(reducer, initialState),
  dispatch,
};
const wrapper = shallow(
  <Map store={store} />,
);
const component = wrapper.find(MapComponent);

describe('<Map />', () => {
  it('should render', () => {
    expect(wrapper).toBeTruthy();
  });

  it('should pass the region as prop', () => {
    expect(component.props().region).toEqual(region);
  });

  it('should pass the markers as prop', () => {
    expect(component.props().markers).toEqual(markers);
  });
  // a signal is an action wich will be handled by a saga
  it('should dispatch an newChan signal', () => {
    component.simulate('longPress', { nativeEvent: { coordinate } });
    expect(dispatch.mock.calls.length).toBe(1);
    expect(dispatch.mock.calls[0][0]).toEqual(sNewChan(coordinate));
  });
  // a message is an action wich will be handled by a reducer
  it('should dispatch a setRegion message', () => {
    component.simulate('regionChange', otherRegion);
    expect(dispatch.mock.calls.length).toBe(2);
    expect(dispatch.mock.calls[1][0]).toEqual(mSetRegion(otherRegion));
  });
});

@markerikson , что ты имел в виду под:

вы можете протестировать свою реализацию mapStateToProps

скажем, вы экспортируете подключенный контейнер через экспорт ES6. У вас не будет доступа к частному файлу mapStateToProps. Вы говорили о каком-то другом способе или можно уточнить?

@мордракс

Вы можете проверить правильность имен свойств

Итак, если вы выставите свой mapStateToProps и сделаете его общедоступным, а затем скажите, что вы визуализируете свой ContainerComponent из своего теста, включая отправку, скажем, поддельного хранилища и отправку свойства состояния, тогда ваш mapStateToProps получает это состояние, сопоставляет его с опорой. Но тогда как вы можете проверить это с этой точки? Connect вызовет mapStateToProps, таким образом объединив реквизиты, но где находится шов и какая точка является швом в коде во время этого процесса/потока, где вы можете перехватить реквизиты, установленные в презентационном компоненте? Я предполагаю, что двойное измельчение, такое как @guatedude2 , может быть одним из способов проверить сопоставленные реквизиты без каких-либо швов...

также на вашем мелководье. Итак, компонент connect() передает что обратно, обертку, верно? А та обертка, которая обертывает презентационный компонент?

@granmoe , можете ли вы объяснить это более подробно, что именно вы имеете в виду:

если у вас также есть покрытие для функций, которые вы передаете в connect, вы должны иметь полное покрытие для вашего компонента в целом.

Я также с @tugorez в том, что касается того, что я хочу протестировать и почему.

@markerikson , такой хороший пример со шпионом. Это одна вещь, которую вы можете проверить, но вам понадобится больше помощников, чтобы проверить «единицу поведения». Просто проверка того, что шпионское действие было вызвано из mapDispatchToProps, на самом деле не говорит нам всей истории о компоненте-контейнере. Он не утверждает ожидаемый результат на основе логики обработчика вашего контейнера.

Недостаточно просто проверить, что вы передали свойства или состояние, вы хотите проверить поведение компонента контейнера. Это означает тестирование двух вещей:

1) компонент-контейнер подключил реквизит к презентационному компоненту, и если да, есть ли определенное состояние, которое, как я ожидаю, будет от презентационного компонента после его рендеринга на основе этого конкретного состояния, переданного реквизитом презентационному компоненту. Кто знает, может быть, появится какой-нибудь тупой разработчик и добавит больше кода в mapStateToProps, вы не всегда можете быть уверены, что он правильно отобразит вещи, в этом смысл тестирования логики контейнера. Хотя на самом деле в mapStateToProps нет никакой логики, кто знает, снова появляется какой-то разработчик и добавляет туда оператор if ... ну, это поведение, которое может все испортить.

2) работает ли логика (поведение) обработчика отправки в контейнере.

@dschinkel :

Типичная практика определения компонентов, подключенных к Redux, — это export default connect()(MyComponent) , а также export MyComponent в качестве именованного экспорта. Точно так же, поскольку mapState — это просто функция, вы также можете export const mapState = () => {} , а затем импортировать и тестировать ее отдельно.

Что касается тестирования «контейнерного компонента» по сравнению с «презентационным компонентом»: общая мысль здесь заключается в том, что вам, вероятно, не нужно беспокоиться о тестировании контейнера по большей части, где «контейнер» === «вывод connect ". У React-Redux уже есть полный набор тестов для того, как это должно работать, и у вас нет причин дублировать эти усилия. Мы _знаем_, что он правильно обрабатывает подписку на магазин, вызовы mapState и mapDispatch и передачу реквизитов обернутому компоненту.

Что _вас_ как потребителя библиотеки будет интересовать, так это то, как ведет себя _ваш_ компонент. На самом деле не должно иметь значения, получает ли ваш собственный компонент реквизиты из обертки connect , теста или чего-то еще — важно, как этот компонент ведет себя с определенным набором реквизитов.

(Кроме того, FWIW, я понимаю, что вы абсолютно эксперт по тестированию, а я нет, но _действительно_ похоже, что вы становитесь немного параноиком :) Если вы обеспокоены тем, что mapState случайно сломался, то пишем для него тесты и идем дальше.)

Теперь, если ваш обернутый компонент затем отображает другие подключенные компоненты внутри него, и особенно если вы выполняете полный рендеринг вместо поверхностного рендеринга, тогда может быть проще протестировать вещи, выполнив const wrapper = mount(<Provider store={testStore}><MyConnectedComponent /></Provider> . Но в целом я считаю, что большую часть времени вы _должны_ иметь возможность тестировать свою функцию mapState и «простой» компонент отдельно, и вам действительно не следует пытаться протестировать подключенную версию только для проверки. что вывод mapState передается обычному компоненту.

Для справки вы можете взглянуть на миниатюрную версию connect , которую Дэн написал некоторое время назад, чтобы проиллюстрировать, что она делает внутри: https://gist.github.com/gaearon/1d19088790e70ac32ea636c025ba424e .

Да, я имею в виду, что если вы экспортируете mapStateToProps и dispatchStateToProps, это будет лучше. Я не хочу проверять, работает ли connect() (сотрудник в моем тесте), определенно нет. Так что это был бы один четкий способ сделать это, экспортировать эти два метода:

пример-контейнер-spec.js

describe('testing mapPropsToState directly', () => {
    it.only('returns expected state', () => {
        const state = {firstName: 'Dave'};
        const output = mapStateToProps(state);

        expect(output.firstName).to.equal('Dave');
    });
});

Контейнер

import React from 'react';
import { connect } from 'react-redux'
import Example from './Example';

export const mapStateToProps = (state) => ({
    firstName: state.firstName
})

export default connect(mapStateToProps)(Example);

Презентация

import React, { Component } from 'react';

export default class Example extends Component {
    render(){
        return (
            <div className='example'>
                {this.props.firstName}
            </div>
        )
    }
}

Опять же, вы также можете сделать что-то подобное с _shallow_ рендерингом и по-прежнему погрузиться в дочерний компонент:

пример-контейнер-spec.js

it('persists firstName to presentation component', () => {
        var state = {firstName: "Dave"};
        const container = mount(<ExampleContainer store={fakeStore(state)}/>),
            example = container.find(Example);

        expect(example.length).to.equal(1);
        expect(example.text()).to.equal(state.firstName);
    });

Контейнер

import React from 'react';
import { connect } from 'react-redux'
import Example from './Example';

const mapStateToProps = (state) => ({
    name: state.firstName
})

export default connect(mapStateToProps)(Example);

Презентация

import React, { Component } from 'react';

export default class Example extends Component {
    render(){
        return (
            <div className='example'>
                {this.props.firstName}
            </div>
        )
    }
}

еще один способ сделать это неглубоко, это сделать двойное мелководье. Мелко на мелком родительском элементе будет мелким дочерний компонент, который его обертывает, примерно так:

it('get child component by double shallow()', () => {
        const container = shallow(<ExampleContainer store={fakeStore({})}/>),
            presentationalChildComponent = container.find(Example).shallow().find('.example');

        expect(presentationalChildComponent).to.have.length(1);
    });

Так что это еще один способ, вы проверяете результат вашего компонента презентации.

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

Я понимаю, что вы абсолютно эксперт по тестированию, а я нет, но похоже, что вы становитесь немного параноиком в отношении вещей :)

нет, нет, правильное тестирование означает, что важны хорошие тесты, которые не являются хрупкими и обеспечивают ценность . Во-первых, никто не является _экспертом_. Все постоянно учатся, некоторые просто не хотят этого признавать из-за своего эго. Это называется «Мастерство программного обеспечения» (он же «Профессионализм»).

Тестирование только поведения, а не тестирование сотрудников (сам connect()), как вы сказали, важно. Важно понять это в первый раз и хорошо протестировать. Очень важно убедиться, что вы не тестируете соавторов, и это иногда требует обсуждения среди коллег-разработчиков.

У вас могут быть хрупкие тесты, и важно, чтобы не было хрупких тестов. К тестированию следует относиться серьезно, а это означает постоянную проверку того, правильно ли вы к нему подходите. Я просто пытаюсь понять свой поток. Я TDD, поэтому важно, как я начну, а также важны хорошие тесты. Поэтому никто не должен чувствовать себя «параноиком», когда пытается понять различные способы тестирования компонентов React.

Готовится новый тестовый репозиторий React...
На самом деле, скоро я поделюсь репозиторием, в котором показаны различные стили, комбинации и подходы к тестированию react-redux. Я думаю, что это поможет людям, потому что я не единственный, кто думает об этом. Снова и снова вы видите, как люди задают одни и те же вопросы о тестировании редукционного контейнера и других компонентов. Документы Redux и документы react-redux не оправдывают этого, им не хватает документации в области тестирования IMO. Это лишь поверхностная информация, и нам нужен хороший репозиторий, демонстрирующий различные подходы к тестированию React, так что я скоро его опубликую.

If anyone would like to contribute to that repo, please get a hold of me , так что я могу добавить тебя в соавторы. Я бы хотел, чтобы люди добавляли примеры. Я хотел бы увидеть примеры с прямым React Test Utils + mocha , с Enzyme , Ava , Jest , лентой и т. д.

Вывод :

Я думаю, что оба подхода, которые мы обсуждали, хороши. Непосредственно протестируйте метод или протестируйте его, как я сделал выше, погрузитесь в него. Иногда вы не хотите основывать свои тесты на определенном методе, потому что вы можете получить хрупкие тесты ... вот почему в прошлом люди были ужалены тестированием. Так что зависит от того, тестировать ли одну функцию или нет, проверяет ли она «модуль». Иногда вы не хотите делать метод общедоступным, и может быть лучше протестировать контракт над этим и оставить некоторые из них закрытыми. Поэтому всегда важно думать о том, что вы часто делаете, когда пишете тесты. В этом нет ничего плохого. Написать хорошие тесты непросто.

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

@markerikson спасибо за ваш вклад, я люблю говорить о тестировании. Хороший материал, чувак. Я не ставил под сомнение ваш опыт, просто я сам не пробовал тестировать mapStateToProps напрямую! Ты неплохо поступаешь из-за того, что не тестируешь ;). Я просто новичок в редукции, все просто, поэтому у меня будут вопросы, которые не просто «касаются поверхности». Мы должны обсуждать тестирование на более низком уровне, чтобы люди понимали, что они делают, как они могут это делать и получают ли они пользу от своего подхода. Для этого вы должны понимать подключение, реакцию-редукцию, провайдера на более низком уровне... чтобы вы знали, как "только тестировать поведение". Я не видел, чтобы многие люди экспортировали mapStateToProps , что мне тоже интересно... и это заставляет задуматься, почему бы и нет.

WeDoTDD.com
Кстати, для всех, кто интересуется, я запустил WeDoTDD.com в прошлом году (кстати, написан на React). Если определенная команда (все разработчики в определенной команде) TDD или вся ваша компания (в некоторых компаниях есть разработчики, где все TDD особенно консалтинговые компании), свяжитесь со мной по адресу slack.wedotdd.com.

Обновлять.

поиграв больше с тестированием подключенных компонентов и подумав о том, что отлично работает для меня, я теперь на 100% согласен с заявлением @markerikson здесь:

если вы действительно чувствуете, что вам нужно протестировать подключенный компонент, вы должны иметь возможность отображать не нужно беспокоиться о провайдере, контексте или чем-то еще.

Я не видел необходимости вводить <Provider /> в свои тесты, и кажется, что вы тестируете соавтора, если делаете это, когда смысл не в том, чтобы проверить, что <Provider /> работает. Вы должны проверить, что поведение в вашем компоненте работает, и не имеет значения, получает ли ваш контейнер хранилище через контекст. Просто передайте магазин в качестве реквизита. Для подключения просто нужен какой-то способ попасть в магазин, ему все равно, как (контекст или реквизит)... полностью избавиться от провайдера в ваших тестах.

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

Протестируйте чистую функцию mapStateToProps напрямую, экспортировав ее в файл-контейнер. Я использую комбинацию тестирования mapStateToProps + также пару тестов, которые поверхностно проверяют мой контейнер, проверяя, что презентационный компонент, по крайней мере, получает ожидаемые реквизиты, а затем я останавливаюсь на тестировании реквизитов. Я также решил не делать двойного измельчения для своих тестов компонентов контейнера. TDD дает мне обратную связь, что, пытаясь сделать это, тесты становятся слишком сложными, и вы, вероятно, делаете что-то не так. Это «неправильно» - «не дублируйте мелкие вещи для ваших тестов компонентов контейнера». Вместо этого создайте новый набор тестов презентационных компонентов, которые независимо проверяют выходные данные ваших презентационных компонентов.

Рад слышать, что у тебя все наладилось. (FWIW, мои комментарии были в основном в ответ на ваши вопросы о «швах» и «копании в реквизите», которые действительно казались ненужными уровнями детализации, практически бесполезными.)

Использование <Provider> /context _будет_ необходимо, если вы выполняете полную визуализацию компонента, который отображает другие подключенные компоненты, поскольку эти подключенные дочерние элементы будут искать хранилище в контексте.

Если у вас есть предложения по улучшению текущей документации Redux по тестированию, обязательно отправьте PR.

@markerikson хорошее замечание по поводу крепления. Я бы сделал монтирование только в том случае, если бы мне нужно было проверить жизненный цикл компонента, который я тестирую. Я бы не стал использовать его для глубокого погружения в дочерние компоненты... которые больше не будут квалифицироваться как модульный тест, а в основном являются интеграционным тестированием, и вы будете связывать этот тест с деталями реализации, когда вам нужно протестировать эти дочерние компоненты на их самостоятельно, а не через родительский компонент.

В любом случае хороший материал, спасибо!

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

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

а ок понял! да не подумал об этом...

Спасибо за все комментарии здесь, @markerikson и @dschinkel! Они очень полезны. Теперь я решил экспортировать свой несвязанный компонент и протестировать его, как и любой другой, а также отдельно протестировать свои mapStateToProps и mapDispatchToProps . Однако есть один случай, когда эти тесты не обнаруживаются, и это когда map(State/Dispatch)ToProps на самом деле не передается в вызов connect . Пример:

import React from 'react';
import { connect } from 'react-redux';

export const mapStateToProps = ({ name }) => ({ name });

export const Example = ({ name }) => <h1>{name}</h1>;

export default connect({ name } => ({ name: firstName }))(Example); // Whoops, didn't actually pass in `mapStateToProps`.

Это, вероятно, никогда не произойдет на практике, но может, особенно потому, что линтеры не будут сообщать mapStateToProps как неиспользуемую переменную, поскольку она экспортируется.

@danny-andrews Не могли бы вы уточнить, как протестировать функцию mapDispatchToProps?

@лейзард

Я больше не тестирую mapDispatchToProps или mapStateToProps напрямую. После этой ветки я передумал и советую этого не делать.

Кроме того, тестируя их, вы столкнетесь с проблемой @danny-andrews, но проблема больше в хороших тестах, и попытка раскрыть эти две функции - не очень хорошая идея. Тестирование закончилось. Вы должны протестировать эти два метода косвенно, проверяя поведение или вместо этого проверяя реквизиты через ваш неглубокий контейнер. Я обнаружил, что нет причин пытаться раскрыть эти частные методы, и теперь я понял, что это также была плохая практика тестирования, чтобы попытаться сделать это.

Поэтому я не согласен с @markerikson , проверьте подключенный компонент.

Найти правильный API/контракт для тестирования и не перетестировать, вот ключевой ребенок :).

@дшинкель

Вы должны протестировать эти два метода косвенно, проверив поведение...
Поэтому я не согласен с @markerikson , проверьте подключенный компонент.

Вы предлагаете полностью отказаться от экспорта несвязанного компонента и протестировать только подключенный компонент? Я думаю , что это «чрезмерное тестирование». Это излишне связывает ваши тесты с деталями реализации тестируемого компонента. Потому что функциональность неподключенного компонента (если он есть, то это совсем другая куча червей) совершенно не связана с тем, откуда он получает свои реквизиты. Возможно, в будущем вы захотите превратить этот компонент в чисто презентационный компонент. Если вы тестируете подключенный компонент, вам придется изменить все свои тесты, чтобы больше не внедрять хранилище, а напрямую передавать реквизиты. Если вы тестировали несвязанный компонент, вам не нужно ничего делать, кроме как сбросить тесты, специфичные для подключенного компонента (подробнее об этом ниже).

Я согласен с тем, что вам не следует напрямую тестировать mapStateToProps / mapDispatchToProps . Предоставление доступа к этим закрытым методам просто для целей тестирования всегда казалось мне запахом кода. Фактически, я думаю, что это единственный раз, когда вы должны протестировать подключенный компонент. Итак, резюмируя:

  1. Экспорт несвязанного компонента и подключенного компонента
  2. НЕ экспортируйте mapStateToProps или mapDispatchToProps
  3. Протестируйте всю логику компонента через неподключенный компонент
  4. Проверяйте подключенный компонент только при тестировании взаимодействия с избыточностью (реквизиты данных передаются из надлежащего места в хранилище/реквизиты действий назначаются соответствующим создателям действий и т. д.).

Единственные дополнительные накладные расходы при выполнении номера 4 заключаются в том, что вам нужно заглушить хранилище избыточности, чтобы передать его в ваш подключенный компонент. Однако это довольно просто, поскольку API состоит всего из трех методов.

Метод MapStateToProps

TestContainer.jsx:

import { connect } from 'react-redux';

export const TestContainer = ({ permissions }) => (permissions['view-message'] ? 'Hello!' : null);

export const mapStateToProps = ({ auth }) => ({ permissions: auth.userPermissions });

export default connect(mapStateToProps)(TestContainer); // Ewww, exposing private methods just for testing

TestContainer.spec.jsx:

import React from 'react';
import { shallow } from 'enzyme';

import TestContainer, { mapStateToProps } from './TestContainer';

it('maps auth.userPermissions to permissions', () => {
  const userPermissions = {};

  const { permissions: actual } = mapStateToProps({
    auth: { userPermissions },
  });

  expect(actual).toBe(userPermissions);
});

Новый метод

TestContainer.jsx:

import { connect } from 'react-redux';

export const TestContainer = ({ permissions }) => (permissions['view-message'] ? 'Hello!' : null);

const mapStateToProps = ({ auth }) => ({ permissions: auth.userPermissions });

export default connect(mapStateToProps)(TestContainer);

TestContainer.spec.jsx:

import React from 'react';
import { shallow } from 'enzyme';

import TestContainer, { TestComponent } from './TestContainer';

it('renders TestComponent with approriate props from store', () => {
  const userPermissions = {};
  // Stubbing out store. Would normally use a helper method. Did it inline for simplicity.
  const store = {
    getState: () => ({
      auth: { userPermissions },
    }),
    dispatch: () => {},
    subscribe: () => {},
  };
  const subject = shallow(<TestContainer store={store} />).find(TestComponent);

  const actual = subject.prop('permissions');
  expect(actual).toBe(userPermissions);
});

да, это то, что я говорю, я не экспортирую и не тестирую mapStateToProps , это тестирование. Протестируйте через API . API здесь — Container .

@ Дэнни-Эндрюс

Протестируйте всю логику компонента через неподключенный компонент

Вы можете привести несколько примеров?

Я имею в виду то же самое, что и вы, когда вы сказали: «Вы должны косвенно протестировать эти два метода, проверив поведение».

Это может помочь уточнить термины: когда я говорю «неподключенный компонент», я имею в виду необработанный компонент, который обернут вызовом connect , а когда я говорю «подключенный компонент», я имею в виду результирующий компонент, который возвращается из вызов connect . «Подключенный компонент» и «контейнер» я считаю синонимами. Из моего выше:

// Unconnected component. Test all component logic by importing this guy.
export const TestComponent = ({ permissions }) => (permissions['view-message'] ? 'Hello!' : null);

const mapStateToProps = ({ auth }) => ({ permissions: auth.userPermissions });

// Connected component (container). Only test interaction with redux by importing this guy.
export default connect(mapStateToProps)(TestComponent);

Некоторые люди предпочли бы полностью разделить их и сделать так, чтобы их "контейнеры" были просто вызовами connect , которые обертывают чисто компонент представления. В этом случае номера 1 и 3 в списке исчезают, и вам нужно написать единственные тесты, которые проверяют взаимодействие с избыточностью (номер 4).

Я планирую написать ОГРОМНУЮ запись в блоге об этом. Я занят в данный момент в коде, но отвечу позже

Прочитав эту ветку несколько раз, я пришел к выводу, что рекомендуется 3 подхода:

  1. Экспортируйте mapDispatchToProps и mapStateToProps и протестируйте их отдельно.
  2. Неглубокий рендеринг компонента Container и проверка правильности подключения.
  3. Используйте селекторы вместо mapStateToProps и создатели действий вместо mapDispatchToProps и тестируйте их отдельно; напишите тесты, используя создателей действий, редукторов и селекторов вместе, чтобы убедиться, что весь поток работает.

В конце концов, я думаю, все варианты допустимы, но имеют свои плюсы и минусы. «Самый чистый», вероятно, второй, но он также включает в себя большую часть работы. Мои личные предпочтения на самом деле идут к варианту 3.
Подробнее об этом я писал в своем блоге , где у меня тоже есть несколько примеров кода.

@укорина
В современном мире модулей ES6 каждый файл считается модулем, заключенным в свою собственную область. Экспорт — это ваш API маленького модуля, и экспортировать методы только для того, чтобы вы могли их протестировать, кажется совершенно неправильным. Это похоже на то, как вы делаете частные методы общедоступными, просто чтобы протестировать их.

Вот почему я склоняюсь к подходу @dschinkel , а не к экспорту mapDispatch и mapState, потому что для меня они являются частной реализацией подключенного компонента.

Однако я не уверен, что делать с компонентом, который только оборачивает другой компонент вокруг соединения.

Вы когда-нибудь писали в блоге? @dschinkel хотел бы это прочитать

@АнаРобинн

Да, я согласен с вами, что просто экспортировать mapDispatch и mapState кажется неправильным, и я, вероятно, не стал бы использовать его сам, однако это допустимый вариант, хотя и менее «чистый». . Я оставил это как возможный вариант еще и потому, что Дэн Абрамов предложил именно эту технику здесь: https://github.com/reduxjs/react-redux/issues/325#issuecomment -199449298.

Чтобы ответить на ваш вопрос «что делать с компонентом, который только оборачивает другой компонент вокруг соединения», я бы сказал, что просто не тестируйте его. В вашем приложении нет бизнес-логики, и вы не хотите тестировать реализацию connect — она уже протестирована в библиотеке react-redux .

Если вы все равно хотите протестировать, чтобы проверить свой код в будущем, вы всегда можете следовать второму подходу, который я описываю здесь, который ближе к тому, что описывает @dschinkel .

@ucorina Точно, я думаю, что я бы тоже выбрал вариант! Спасибо за более подробное объяснение вашего ответа. Это красиво написанный пост в блоге. Поздравляю!

@ucorina Извините, что снова беспокою, но, поспав некоторое время, я тоже не уверен, что полностью согласен с вариантом 2. Хотя я не уверен. Я также не уверен в пользе тестирования mapStateToProps.

Как вы думаете, это нормально, что «Вариант 2» косвенно также проверяет создателей экшена? А также косвенное тестирование селекторов? Это хорошая вещь?
Я не на 100% убежден в этом, но я также сбит с толку всей (неверной) информацией. Существует так много разных идей для тестирования.

  1. Используйте селекторы только внутри mapStateToProps

    • издевайтесь над ними во время тестов контейнера, если это необходимо

    • Тестовые селекторы отдельно.

    • Используйте создателей действий только внутри mapDispatchToProps

    • издевайтесь над ними во время тестов контейнера, если это необходимо.

    • Тестируйте создателей действий отдельно.

    • Мелко связанный компонент

    • утверждать, что это отображается

    • проверить любую дополнительную логику, которая должна была быть добавлена ​​к mapStateToProps , mapDispatchToProps или mergeProps и еще не проверена где-либо еще. (условные отправки, селекторы, которые принимают аргументы из ownProps и т. д.)



      • в этом случае вы только проверяете, что макеты вызывались правильное количество раз и с правильными аргументами.



Альтернатива:

  • Моделируйте состояние хранилища для совместного тестирования контейнера и селекторов.
  • Смоделируйте внешние вызовы API для совместного тестирования контейнера и действий.

@AnaRobynn Я считаю, что второй подход @ucorina правильный.

Многое из того, что я слышу, предполагает:

  1. Вы должны выставить mapDispatchToProps и протестировать его напрямую.
  2. Вам не нужно тестировать вызов connect(), потому что connect() уже протестирован.

Что касается 1, я не думаю, что это хорошая практика - раскрывать внутренности для тестирования. Ваши тесты A: теперь предполагают внутреннюю структуру, а B: должны проверять то, как они будут использоваться на самом деле.

Что касается 2, вы _абсолютно_ должны протестировать что-то, что вызывает внешнюю библиотеку. Это одно из слабых мест в любом приложении для взлома. С любой библиотекой все еще возможно, что критические изменения будут внесены в второстепенную версию/патч-версию, и вы _хотите_ чтобы ваши тесты быстро терпели неудачу.

Как вы думаете, это нормально, что «Вариант 2» косвенно также проверяет создателей экшена? А также косвенное тестирование селекторов? Это хорошая вещь?

Да, избыточное тестовое покрытие — это хорошо.

@philihp @dougbacelar
Я считаю, что немного вырос в связи с тестированием приложений React. Мои текущие убеждения и мысли по этому поводу:

  1. Не раскрывайте mapStateToProps и mapDispatchToProps , если вам не нужны эти сопоставления в нескольких компонентах. Экспортировать их только для тестирования — это анти-шаблон.
  2. В настоящее время я рассматриваю контейнеры как сотрудников (функции, которые делегируют другие функции, и эти другие функции выполняют реальную логику).
    Что это значит?
  3. Контейнеры не выполняют никакой логики
  4. Логика выбора состояния предназначена для функций селектора (которые могут быть чисто модульно протестированы).
  5. Логика действий скрыта в создателях действий (что может зависеть от того, используете ли вы redux-thunk или не являетесь соавторами сами по себе)
  6. Визуализированное представление — это еще одна функция (которая потенциально имеет другую функцию соавтора или просто отображает материал).

=> Настройка теста на самом деле довольно проста, если вы используете соответствующие библиотеки тестирования (я рекомендую testdouble.js).
=> Макет вашего селектора и действий через testdouble.js (используя td.when() )
=> Поверхностный рендеринг вашего компонента-контейнера, dive() один раз, чтобы получить доступ к методам компонента представления
=> Учитывая набор правил td.when() , убедитесь, что компонент ведет себя правильно

Не нужно вводить какую-то библиотеку fakeStore, можно заглушить магазин.

Пример:

/** <strong i="27">@format</strong> */

import React from 'react';
import { shallow } from 'enzyme';

function setup(props) {
  const HasOrganization = require('./HasOrganization').default;
  const defaultProps = {
    store: {
      subscribe: Function.prototype,
      getState: Function.prototype,
      dispatch: Function.prototype,
    },
  };
  const container = shallow(<HasOrganization {...defaultProps} {...props} />);
  return {
    container,
    wrapper: container.dive(),
  };
}

describe('<HasOrganization /> collaborations', () => {
  let getCurrentOrganizationId;
  beforeEach(() => {
    getCurrentOrganizationId = td.replace('../containers/App/userSelectors')
      .selectCurrentOrganizationId;
  });

  test('not render anything when the id is not valid', () => {
    const Dummy = <div />;
    td.when(getCurrentOrganizationId()).thenReturn(null);
    const { wrapper } = setup({ children: Dummy });

    expect(wrapper.find('div').length).toBe(0);
  });

  test('render something when the id is valid', () => {
    const Dummy = <div />;
    td.when(getCurrentOrganizationId()).thenReturn(1);
    const { wrapper } = setup({ children: Dummy });

    expect(wrapper.find('div').length).toBe(1);
  });
});

Мысли?

@AnaRobynn Полностью согласен с пунктами 1 и 2!

Мои контейнеры обычно очень прямолинейны:

// imports hidden

const mapStateToProps = (state, { userId }) => ({
  isLoading: isLoading(state),
  hasError: hasError(state),
  placeholders: getPlaceholders(userId)(state), // a list of some sort
  shouldDoStuff: shouldDoStuff(state),
});

const mapDispatchToProps = {
  asyncActionPlaceholder,
};

export const mergeProps = (stateProps, dispatchProps, ownProps) => {
  return {
    ...stateProps,
    ...dispatchProps,
    ...ownProps,
    onMount: () => {
      if (stateProps.shouldDoStuff) {
        dispatchProps.asyncActionPlaceholder(ownProps.userId);
      }
    },
  };
};

export default compose(
  connect(
    mapStateToProps,
    mapDispatchToProps,
    mergeProps
  ),
  callOnMount(props => props.onMount())
)(SampleComponent);

Так что я бы

  1. шпионить за всеми селекторами и создателем действий
  2. test getPlaceholders шпион был вызван с правильным идентификатором пользователя
  3. assert SampleComponent был отрендерен с правильными реквизитами
  4. вызовите реквизит onMount из SampleComponent и убедитесь, что он отправляет фиктивное действие, когда shouldDoStuff===true
  • Такие вещи, как условный рендеринг вещей в зависимости от реквизита, я бы протестировал в отдельном тесте за SampleComponent
  • Я также предпочитаю использовать redux-mock-store для издевательства над магазином, например, configureMockStore([thunk])() , но думаю, что можно этого не делать...

TL;DR Я в основном вызывал только тестовые селекторы с правильными аргументами, имитированные действия были успешно отправлены (если они зависят от какой-либо логики) и что дочерний компонент отображается с правильными реквизитами.

@филихп

  • Я категорически против тестирования внешних библиотек, предпочитаю издеваться над всем и тестировать изолированно.
  • Я чувствую себя уверенно, полагая, что сквозные тесты выявят любые серьезные проблемы, вызванные обновлением библиотеки.
  • Причина, по которой я не люблю тестировать контейнеры + селекторы + создатели действий вместе, заключается в том, что вам придется повторно тестировать одни и те же селекторы и создатели действий для каждого другого контейнера, который их повторно использует. В этот момент вам просто лучше добавить больше тестовых случаев в сами модульные тесты.

@dougbacelar Можете ли вы дать пример тестового кода для проверки показанного вами компонента. Вы используете фермент? Что-то другое? Мелкий? Устанавливать?

@дугбаселар

Я категорически против тестирования внешних библиотек, предпочитаю издеваться над всем и тестировать изолированно.

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

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

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

Да , также проверьте свои контейнеры. Если это означает, что они обеспечивают частичное покрытие селекторов и создателей действий, это неплохо.

Вот что я имею в виду...

import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { saveColor } from '../actions/save';
import { selectColors } from '../reducers/colors.js';
import ColorButtons from '../components/ColorButtons';
const ColorButtons = ({ colors, onClick }) => (
  <div>
    {colors.map(color => {
      <button type="button" key={color} onClick={onClick(color)}>{color}</button>
    })}
  </div>
);
ColorButtons.propTypes = {
  colors: PropTypes.arrayOf(PropTypes.string),
  onClick: PropTypes.func.isRequired,
};
const mapStateToProps = state => ({
  colors: selectColors(state.colors),
});
const mapDispatchToProps = dispatch => ({
  onClickColor: color => () => {
    dispatch(saveColor({ color }));
  },
});
export default connect(
  mapStateToProps,
  mapDispatchToProps
)(ColorButtons);
import React from 'react';
import { shallow } from 'enzyme';
import configureStore from 'redux-mock-store';
import ColorButtons from '../ColorButtons';
import { saveColor } from '../../actions/save';
import { selectColors } from '../../reducers/colors.js';
const buildStore = configureStore();
describe('<ColorButtons />', () => {
  let store;
  let wrapper;
  const initialState = { colors: ['red', 'blue'] };
  beforeEach(() => {
    store = buildStore(initialState);
    wrapper = shallow();
  });
  it('passes colors from state', () => {
    expect(wrapper.props().colors).toEqual(selectColors(initialState.colors));
  });
  it('can click yellow', () => {
    const color = 'yellow';
    wrapper.props().onClick(color)();
    expect(store.getActions()).toContainEqual(saveColor({ color }));
  });
});

Здесь не проверяются все различные параметры, которые могут получить selectColors и saveColor ; это проверка того, что при создании <ColorButtons /> он получает ожидаемые свойства. Обязательно проверьте их в другом тесте.

import { selectColors } from '../colors.js';
describe('selectColors', () => {
  it('returns the colors', state => {
    expect(selectColors(['red'])).toEqual(['red'])
  })
  it('returns an empty array if null', state => {
    expect(selectColors(null)).toEqual([])
  })
  it('returns a flattened array', state => {
    expect(selectColors(['red', ['blue', 'cyan']])).toEqual(['red','blue','cyan'])
  })
})

Я категорически против тестирования внешних библиотек, предпочитаю издеваться над всем и тестировать изолированно.

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

Не согласен, частично.
Я никогда не издеваюсь над внешними библиотеками (не издеваюсь над тем, чего у вас нет), но я пишу оболочку, например, для axios. В моем совместном тесте я могу просто смоделировать все функции и убедиться, что все подключено правильно.

В моих совместных тестах я всегда имитирую модули, функции и т. д.
Эти функции, вызываемые вашим соавтором, можно легко протестировать.

Вот что я имею в виду...

Тоже не согласен.
Здесь вы неявно тестируете свои редукторы и селекторы. Если это то, что вы хотите сделать, хорошо, но я предпочитаю издеваться над этими функциями и просто проверять, правильно ли они вызываются. Остальное обрабатывается редьюсерами/селекторами.

В этой ветке перефразируются пункты, сделанные в https://martinfowler.com/articles/mocksArentStubs.html , классическом и фиктивном тестировании.

Согласованный. Блокировка.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги