Тестирование связанных компонентов с ферментом - PullRequest
0 голосов
/ 08 апреля 2019

Я учусь брать этот курс тестирования для тестирования подключенных компонентов, настроив store factory помощника по тестированию, который «создает хранилище для тестирования, соответствующее конфигурации нашего хранилища». Ниже вы можете увидеть мой подключенный образец компонента, а также код, используемый для настройки тестов, в котором я создаю подключенную мелкую ферментную оболочку для моего образца компонента. Однако, похоже, что исходное состояние, которое я передаю компоненту примера, в этом случае {jotto: 'foo'} не передается моему компоненту образца при создании этой мелкой оболочки. Я делаю что-то не так и как правильно воссоздать необходимую конфигурацию магазина при запуске ферментных тестов? Спасибо!

Пример компонента:

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

const SampleComponent = (props) => {
  console.log(props);
  return (
    <div>This is a sample component!</div>
  );
};

const mapStateToProps = (state) => ({
  jotto: state.jotto,
});

export default connect(mapStateToProps)(SampleComponent);

Редуктор:

import * as jottoActionTypes from 'actionTypes/jottoActionTypes';

export const initialState = {
  isSuccess: false,
};

const jotto = (state = initialState, action) => {
  switch (action.type) {
    case jottoActionTypes.CORRECT_GUESS:
      return {
        ...state,
        isSuccess: true,
      };
    default:
      return state;
  }
};

export default jotto;

корневой редуктор:

import {combineReducers} from 'redux';
import {connectRouter} from 'connected-react-router';
import jotto from 'reducers/jottoReducer';

export default (historyObject) => combineReducers({
  jotto,
  router: connectRouter(historyObject),
});

Настройка теста:

import React from 'react';
import {shallow} from 'enzyme';
import {createStore} from 'redux';
import rootReducer from 'reducers/rootReducer';
import SampleComponent from './sampleComponent';

export const storeFactory = (initialState) => createStore(rootReducer, initialState);

const store = storeFactory({jotto: 'foo'});
const wrapper = shallow(<SampleComponent store={store} />).dive();
console.log(wrapper.debug());

// Result:
      { store:
         { dispatch: [Function: dispatch],
           subscribe: [Function: subscribe],
           getState: [Function: getState],
           replaceReducer: [Function: replaceReducer],
           [Symbol(observable)]: [Function: observable] },
        jotto: undefined,
        dispatch: [Function: dispatch],
        storeSubscription:
         Subscription {
           store:
            { dispatch: [Function: dispatch],
              subscribe: [Function: subscribe],
              getState: [Function: getState],
              replaceReducer: [Function: replaceReducer],
              [Symbol(observable)]: [Function: observable] },
           parentSub: undefined,
           onStateChange: [Function: bound onStateChange],
           unsubscribe: [Function: unsubscribe],
           listeners:
            { clear: [Function: clear],
              notify: [Function: notify],
              get: [Function: get],
              subscribe: [Function: subscribe] } } }

Ответы [ 2 ]

1 голос
/ 09 апреля 2019

Просто немного об этом курсе Удеми ... это не самый лучший инструмент обучения. Инструктор подходит к тестированию, используя data attributes, которые не нужны для тестирования jest и enzyme (они также заполняют DOM неиспользуемыми атрибутами).

Кроме того, ее опыт работы с кодом находится на начальном уровне, и она делает довольно много ошибок и делает странный выбор кода. Тем не менее, узнайте, что вы можете из него, и начните изучать тесты, созданные теми, кто поддерживает популярные пакеты npm (большинство хорошо документированных и популярных пакетов будут содержать тесты, которые научат вас более практическому подходу unit и integration тестирование).

Во всяком случае, я отвлекся, у вас есть два варианта для тестирования container:

  1. export class / pure function, shallow или mount оберните его и обновите его поддельными подпорками (очень легко, меньше головной боли и более распространено)
  2. Оберните ваш компонент в <Provider> в формате dupx и <MemoryRouter> в router-router-dom, а затем mount it (может стать очень сложным, поскольку требует полуглубокого понимания: энзима и того, как он интерпретирует DOM когда компонент смонтирован, последовательность действий / редуктора Redux, как создавать фиктивные реализации и / или фиктивные файлы и как правильно обрабатывать действия promise)

Рабочие примеры (перейдите на вкладку Tests для запуска тестов; найдите .tests.js в каталогах, указанных ниже):

Edit Testing Redux Component


Примечание: Codesandbox в настоящее время имеет некоторые ограничения тестирования, как указано ниже, поэтому, пожалуйста, настройте его для вашего локального проекта.

Containers / Dashboard / __ tests __ / UnconnectedDashboard.test.js (вы можете просто mount обернуть этот неподключенный компонент, чтобы утверждать его глубоко вложенные дочерние узлы)

import { Dashboard } from "../index.js";

/* 
   codesandbox doesn't currently support mocking, so it's making real
   calls to the API; as a result, the lifecycle methods have been
   disabled to prevent this, and that's why I'm manually calling
   componentDidMount.
*/

const getCurrentProfile = jest.fn();

const fakeUser = {
  id: 1,
  name: "Leanne Graham",
  username: "Bret",
  email: "Sincere@april.biz",
  address: {
    street: "Kulas Light",
    suite: "Apt. 556",
    city: "Gwenborough",
    zipcode: "92998-3874",
    geo: {
      lat: "-37.3159",
      lng: "81.1496"
    }
  },
  phone: "1-770-736-8031 x56442",
  website: "hildegard.org",
  company: {
    name: "Romaguera-Crona",
    catchPhrase: "Multi-layered client-server neural-net",
    bs: "harness real-time e-markets"
  }
};

const initialProps = {
  getCurrentProfile,
  currentUser: {},
  isLoading: true
};

describe("Unconnected Dashboard Component", () => {
  let wrapper;
  beforeEach(() => {
    wrapper = shallow(<Dashboard {...initialProps} />);
    wrapper.instance().componentDidMount();
  });

  afterEach(() => wrapper.unmount());

  it("initially renders a spinnner", () => {
    expect(getCurrentProfile).toHaveBeenCalled();
    expect(wrapper.find("Spinner")).toHaveLength(1);
  });

  it("displays the current user", () => {
    wrapper.setProps({ currentUser: fakeUser, isLoading: false });
    expect(getCurrentProfile).toHaveBeenCalled();
    expect(wrapper.find("DisplayUser")).toHaveLength(1);
  });

  it("displays a signup message if no users exist", () => {
    wrapper.setProps({ isLoading: false });
    expect(getCurrentProfile).toHaveBeenCalled();
    expect(wrapper.find("DisplaySignUp")).toHaveLength(1);
  });
});

контейнеры / Панель управления / __ тесты __ / ConnectedDashboard.test.js

import Dashboard from "../index";
// import { getCurrentProfile } from "../../../actions/profileActions";
import * as types from "../../../types";

/* 
  codesandbox doesn't currently support mocking, so it's making real
  calls to the API; however, actions like getCurrentProfile, should be
  mocked as shown below -- in your case, you wouldn't need to use
  a promise, but instead just mock the "guessedWord" action and return
  store.dispatch({ ... })
*/

const fakeUser = {
  id: 1,
  name: "Leanne Graham",
  username: "Bret",
  email: "Sincere@april.biz",
  address: {
    street: "Kulas Light",
    suite: "Apt. 556",
    city: "Gwenborough",
    zipcode: "92998-3874",
    geo: {
      lat: "-37.3159",
      lng: "81.1496"
    }
  },
  phone: "1-770-736-8031 x56442",
  website: "hildegard.org",
  company: {
    name: "Romaguera-Crona",
    catchPhrase: "Multi-layered client-server neural-net",
    bs: "harness real-time e-markets"
  }
};

const flushPromises = () => new Promise(resolve => setImmediate(resolve));

describe("Connected Dashboard Component", () => {
  let store;
  let wrapper;
  beforeEach(() => {
    store = createStoreFactory();
    wrapper = mount(
      <Provider store={store}>
        <MemoryRouter>
          <Dashboard />
        </MemoryRouter>
      </Provider>
    );
  });

  afterEach(() => wrapper.unmount());

  it("initially displays a spinner", () => {
    expect(wrapper.find("Spinner")).toHaveLength(1);
  });

  it("displays the current user after a successful API call", async () => {
    /* 
      getCurrentProfile.mockImplementationOnce(() => new Promise(resolve => {
        resolve(
          store.dispatch({
            type: types.SET_SIGNEDIN_USER,
            payload: fakeUser
          })
        );
      });

      await flushPromises();
      wrapper.update();

      expect(wrapper.find("DisplayUser")).toHaveLength(1);
    */

    store.dispatch({
      type: types.SET_SIGNEDIN_USER,
      payload: fakeUser
    });

    wrapper.update();

    expect(wrapper.find("DisplayUser")).toHaveLength(1);
  });

  it("displays a signup message if no users exist", async () => {
    /* 
      getCurrentProfile.mockImplementationOnce(() => new Promise((resolve,reject) => {
        reject(
          store.dispatch({
            type: types.FAILED_SIGNEDIN_USER
          })
        );
      });

      await flushPromises();
      wrapper.update();

      expect(wrapper.find("DisplaySignUp")).toHaveLength(1);
    */

    store.dispatch({
      type: types.FAILED_SIGNEDIN_USER
    });

    wrapper.update();

    expect(wrapper.find("DisplaySignUp")).toHaveLength(1);
  });
});
0 голосов
/ 11 апреля 2019

Решение: Я забыл параметр браузера для моего корневого редуктора, учитывая, что я использовал connected-react-router.

import rootReducer from 'reducers/rootReducer';
import {createBrowserHistory} from 'history';


export const storeFactory = (initialState) => createStore(rootReducer(createBrowserHistory()), initialState);
...