Рубрики
Без рубрики

TDD’ing реализация MapDispatchtoprops

Пример теста, принудительный на TDD, реализующий mapdispatchtonops.

Автор оригинала: Dave Schinkel.

Мне нравится мои racte.js презентационные (глупые) компоненты (и, следовательно, соответствующие файлы .js), чтобы быть Действительно тупой. Все, что мне нравится видеть в них, это JSX, комментарий реагирования и реквизит … Вот и все. Я Не Хотите эти файлы держать любое поведение или локальное состояние. Я не хочу, чтобы методы жизненного цикла в реагировании или другое поведение запутало мои презентационные компоненты. Состояние локального компонента, любое поведение компонентов реагирования на реакцию и т. Д. Может поддерживать/централизовать в других частях нашего интерфейса такого использования, такого как соединенные компоненты контейнера, создатели действия и т. Д.

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

Прежде чем мы начнем, давайте притворяться, что кто-то ранее уже TDD’D A составная часть. И тест, который они написали, что заставило их в ETIE, чтобы реализовать этот код был этот тест:

describe('Login Container', () => {
  it('shows Login', () => {
    const loginContainer = shallow(),
      login = loginContainer.dive().find(Login)
      
      expect(login).to.exist
  })
})

История/функция для этого сообщения в блоге, которые нам нужно начать работать, называется Пользователь может войти Отказ

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

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

Быстрое примечание: этот пост блога показывает только что-то исходное поведение для этого. Дальнейшая реализация для обработчика входа в систему будет жить в нашем файле действий Creator .js, редукторы и т. Д. Мы не пойдем так далеко в этом сообщении в блоге. Последующие тесты действий, тесты редукторов и т. Д. Мы заставляем нас вымыть/TDD о том поведении/реализации для этой истории.

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

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

Ну, это может быть хорошим:

  it('passes login handler to Login', () => {
  })

Хорошо, так что давайте подумаем об этом тесте на секунду.

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

Теперь, прежде чем мы напишем это утверждение. Имейте в виду, что мы знаем некоторые знания о том, как React-redux Работает, поэтому я не только что придумал это утверждение из воздуха.

Давайте начнем с написания утверждения, что утверждает что-то, что мы желаем, чтобы мы работали, что мы еще не работаем (aka желаемое мышление в TDD):

it('passes login handler to Login', () => {
  expect(container.find(Login).props().login).to.exist
})

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

Что я желаю здесь, так это то, что который в настоящее время рендит Успешно передал обработчик входа через реквизит к Отказ

Когда мы начнем новый тест в цикле TDD, мы всегда Проведите наш тест сначала, чтобы увидеть, что он не удается. Если это не удается, это не хороший тест. Если это не сбой, это означает одну из двух вещей:

  • Это зеленое, но это потому, что у вас есть ложный положительный
  • или Вы пишете тест на код, который уже существует … вы не TDD’инг, вы пишете тесты после чего не то, что мы хотим делать здесь

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

Снятый на экран 2017-05-20 на 2,39,48 am.png

Хм, результаты теста говорят мне, что тест не удается, потому что он не знает, что Контейнер средства. Мы знаем, что у нас уже есть Доступно нам, как упоминалось ранее, но как мы можем с ней работать? И Как мы будем работать с контейнером в Изолированные мода? Ну, фермент имеет Неглубоко () Метод, который идеально подходит для создания изолированных модульных испытаний.

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

it('passes login handler to Login', () => {
  const container = shallow()
  expect(container.find(Login).props().login).to.exist
})

Здесь я неглубокий рендеринг и отправка в фиктивное хранилище, потому что мне не волнует магазин; Магазин в этом случае только коллаборатор, не то, что мы пытаемся проверить (SUT). Я забочусь о том, чтобы быть в состоянии работать с контейнером и иметь возможность реализовать поведение в этом контейнере, который передает обработчик входа в мой составная часть. Итак, по манекен Я имею в виду: «Давайте скажем нашему коду, чтобы игнорировать эту часть, нам все равно, и мы не хотим, чтобы он мешал тем, что мы пытаемся проверить».

Неглубокий рендеринг гарантирует, что мы ходим только Один уровень глубокий на любой компонент мы тестируем. То есть … Мы хотим убедиться, что мы тестируем только поведение компонента SUT, а не каким-либо из его дочерних компонентов/поведения.

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

Вот почему эти шутки – это Интеграция Тесты не единица Тесты. Они хрупкие в том, что они тестируют несколько компонентов в дереве реагирования на реагирование вашего приложения. Это нормально для интеграционных сетей, но они не имеют смысла и не являются заменой для изолированных модульных тестов. И TBH Я не большой поклонник шумах шума по нескольким причинам, я не буду уточнить в этом посте. Я предпочитаю написать свои собственные интеграционные тесты, так как у меня есть на любом языке, который я написал; Они на самом деле не так уж большие сделки, чтобы написать, как только вы получите это. Я до сих пор пишу тесты интеграции с Mocha и ферментом или Mocha и Supertest в зависимости от того, какой тест интеграции я пишу.

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

  • Гора ()
  • оказывать ()
  • неглубокий () Тогда нырять ()
  • неглубокий (), Тогда неглубокий () Опять (двойные мелкие … мы неглуемся контейнер и неглубокий логин, мы не хотим этого)

Теперь вернемся к тесту. Давайте снова запустим это:

it('passes login handler to Login', () => {
  const container = shallow()
  expect(container.find(Login).props().login).to.exist
})

Эх! Еще ошибка:

Выстрел на экране 2017-05-20 на 2,46,20 am.png

Нет Biggie, мы добиваемся прогресса, потому что мы знаем, что нам нужно сделать дальше … разрешить эту следующую ошибку. Но я позволю тебе немного секрет. Ферментные породы, но иногда его сообщения об ошибках не. Работав с ферментом на некоторое время, теперь я могу сказать вам, что это:

Method "props" is only meant to be run on a single node. 0 found instead.

Хорошо, что должна была сказала команда ферментов? реквизит () не может быть запущен на undefined или что-то типа того. Это означает, что он не нашел компонент входа в систему, а затем он пытался запустить реквизит () на undefined ... так что это подрывается.

Хорошо, хм … почему это не мог найти Компонент в мелководье Подключенный компонент?

Ну, давайте проверим несколько вещей:

  • Имеет Компонент даже был создан еще?
  • Если это так, мы сказали бы оказывать еще?

давай посмотрим:

class LoginContainer extends Component {
  render(){
    return(  )
  }
}

export { Login }
export default connect(undefined, mapDispatchToProps)(LoginContainer)

Оказывается, у нас есть. Если вы вспомните, у нас был предыдущий тест, который был написан в какой-то момент в прошлом другим человеком TDD’ing, который заставлял тот, кто кодирует на момент создания Компонент и также убедитесь, что контейнер оказывает его. Этот тест был тестом, который мы видели в начале:

  it('shows Login', () => {
    const loginContainer = shallow(),
      login = loginContainer.dive().find(Login)
      expect(login).to.exist
  })

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

Хорошо, хорошо, если эти две вещи не вызывают ошибку, что такое? Оказывается, что мы на самом деле сделать нужно позвонить ферменту нырять () Способ нашего намного оказанного контейнерного компонента. Да, да, я знаю. Я сказал тебе не нырять () … что это сделало бы это Интеграция контрольная работа. Ну не так быстро. По-разному. Иногда у вас есть странные ситуации. И это зависит от того, что вы ныряете в Отказ Так что использую нырять () Является ли решение о том, имеет ли смысл делать это или нет.

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

Так что в этом случае это безопасный к нырять () Отказ Это все еще является изолированный тест подразделения, потому что у меня нет выбора, кроме нырять () Чтобы иметь возможность работать с этим неглубоко визуализированным связан Контейнерный компонент. Почему ты спрашиваешь? Ключевое слово здесь связан , означающий React-redux’s Подключиться () магия. Потому что когда мы неглым оказываем связан контейнер:

const loginContainer = shallow()

Что мы вернулись, было на самом деле больше, чем вы понимаете, потому что мы используем фермент и React-redux. Мы вернулись Ферментная обертка API это обертывает Подключите обертку это обертывание наших мелководных Думаю Я все это правильно). Я знаю, что ваша голова, вероятно, вращается, но любой, кто работал с Redux некоторое время, знает, что удивительно, что я имею в виду. Если вы новичок в Redux, ваша голова будет вращаться; Продолжайте практиковать с React-redux.

Поэтому из-за этой уникальной ситуации нам нужно нырять () Чтобы получить уровень и пройти мимо методов сотрудничества (методы, которые мы не реализуем, у нас нет контроля, и что мы не тестируем). Мы не хотим работать с оболочкой Connect, мы хотим погрузиться и работать с компонентом контейнера и Тогда Сделайте нашу находку на контейнерном компоненте. Так вот почему. Это все еще изолированный тест на единицу. Это просто здесь, мы использовали нырять () Пройти мимо волшебства React – Redux/ Подключите функцию обертки Таким образом, мы можем работать с нашими Отказ

Теперь вы можете спросить, когда бы …| нырять () сделать свой тест превратиться в Интеграция контрольная работа?

Ну, если бы вы были на мелководье, сделайте не подключенный компонент React React, скажите презентационный компонент и сделал фермент Найти () На нем проверить, содержит ли этот компонент какой-то дочерний компонент. И сделал нырять () на этом ребенке; Теперь вы наступили до 00 далеко по дереву компонента: (неглубоко () => Найти () => ныряющий ()) Вы вступаете в реализацию этого ребенка, и вы не хотите делать это для изолированных модульных тестов. Это хорошо для теста интеграции, но мы не пишем Интеграция Испытания прямо сейчас, мы имеем нашу реализацию, написав Изолированные тесты единиц Отказ

Извините за рулевое управление на другой путь. Давайте вернемся к тесту и получите это прохождение.

Хорошо, мы знаем одну вещь, которую нам нужно сделать, это добавить нырять () Так что давайте сделаем это:

  it('passes login handler to Login', () => { 
    const container = shallow()
    expect(container.dive().find(Login).props().login).to.exist
  })

Так что теперь у нас есть Container.dive (). Найти (логин) , не Container.find (вход) Отказ Давайте снова запустим наш тест:

Снятый на экран 2017-05-20 на 3,04,46 AM.PNG

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

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

Прямо сейчас, как он стоит, вот наш компонент:

class LoginContainer extends Component {
  constructor(props) {
    super(props)
  }
  render(){
    return (  )
  }
}

export { Login }
export default connect(undefined, mapDispatchToProps)(LoginContainer)

Давайте реализуем mapdispatchtopops Теперь и получите этот тест, чтобы пройти:

import { login } from '../../actions/UserActions'

class LoginContainer extends Component {
  render(){
     return(  )
  }
}

 const mapDispatchToProps = {
   login: login
 }

export { Login }
export default connect(undefined, mapDispatchToProps)(LoginContainer)

Хорошо, я добавил 3 Вещи здесь:

import { login } from '../../actions/UserActions' 

Pass Concaster’s Props.Login к компоненту для входа в систему, который он доступен для вызова из нашей кнопки OnClick Event

 

Это на самом деле добавит в систему входа в контейнер, поскольку мы подключаемся к нашему контейнеру, не наше Вход Компонент через Connect ():

mapDispatchToProps = {
 login: login
}

Примечание: мы определили нашу Вход Функция обработчика в Usersiaction (Иногда приятно сохранять ваши акции создателями и связанными с этим обработчиками). В этом случае Войти (электронная почта, пароль) собирается получить некоторые данные, сделать несколько рассылки И в конечном итоге наш редуктор обновит состояние с данными, возвращающимися из fetch () вызов.

Прохладный. Давайте снова запустим наш тест, это становится зеленым?

Это сделал! Наш тест заставил нас тестировать привод реализацию MapDispatchtOponops, а также минимальная логика, чтобы пройти обработчик входа в систему для Ура! И, мы знаем, что это работает, потому что мы потерпели неудачу сначала, затем сделали его зеленым! И теперь мы покрыты нашими тестами!

Снятый на экран 2017-05-20 на 3,10,20 am.png

Кроме того, наш код сохраняется на каждом тесте, который мы пишем, потому что мы следуем правилам TDD:

  • Наш тест очень маленький
  • Мы только пишу достаточно продукции, чтобы сделать тестовый пропуск
  • Мы часто рефакторируем наши тесты и производственный код (чистый код/архитектуринг) по пути

Что вы найдете, это вы, как правило, вы обычно в конечном итоге с меньшим количеством реализации, когда TDD, а не записать тесты после или не писать их вообще.

В чем интересно, это оригинально, я планировал на TDD’ing сама MapDispatchtoPonops (в том числе экспортируя его, чтобы сделать его публике, чтобы это возможно), но обнаружил, что мне не нужно этого делать.

Какой следующий тест? Я позволю тебе обдумать это. Но это будет тест, который заставляет нас создавать немного больше поведения. Это будет тест, который заставляет нас начать реализацию этого обработчика входа.

Если вам понравилось этот пост, я бы хотел, чтобы вы выразили, что помогая Пожертвовать/поддерживать отличный ресурс, который я начал Отказ

Какой пост вы хотели бы увидеть дальше? Чирикать мне на @Daveschinkel.

Ваше здоровье!