2017-01-13 13 views
9

У меня есть следующий Redux создатель действий:Как модульное тестирование mapDispatchToProps с Thunk действием

export const keyDown = key => (dispatch, getState) => { 
    const { modifier } = getState().data; 
    dispatch({ type: KEYDOWN, key }); 
    return handle(modifier, key); // Returns true or false 
}; 

И следующий подключенный компонент:

export const mapDispatchToProps = dispatch => ({ 
    onKeyDown: e => { 
     if(e.target.tagName === "INPUT") return; 
     const handledKey = dispatch(keyDown(e.keyCode)); 
     if(handledKey) { 
      e.preventDefault(); 
     } 
    } 
}); 

Я пытаюсь написать тест, чтобы убедиться, что dispatch вызывается с действием keyDown, когда tagName является чем-то другим, кроме "INPUT". Это мой тест:

import { spy } from "sinon"; 
import keycode from "keycodes"; 
import { mapDispatchToProps } from "./connected-component"; 
import { keyDown } from "./actions"; 

// Creates a simple Event stub... 
const createEvent = (tag, keyCode) => ({ 
    target: { 
     tagName: tag.toUpperCase() 
    }, 
    preventDefault: spy(), 
    keyCode 
}); 

it("Dispatches a keyDown event with the specified keyCode if the selected element is not an <input>",() => { 
    const dispatch = spy(); 
    const keyCode = keycode("u"); 
    mapDispatchToProps(dispatch).onKeyDown(createEvent("div", keyCode)); 
    // This fails... 
    expect(dispatch).to.have.been.calledWith(keyDown(keycode)); 
}); 

Предположительно это как-то связано с использованием функций стрелок? Есть ли способ гарантировать, что отправка была вызвана с сигнатурой функции, которую я ожидаю?

+0

Так вы тестируете, что строковое равенство и оператор return работают, или что разработчик не случайно удалил его? Боже, мне не нравятся большинство модульных тестов :( –

+0

Прежде всего, что 'dispatch' на самом деле называется. Много раз я вызываю создателя действия, не переходя к отправке. Проверяя, что действие keyDown также передано, тоже важно:« ожидать (отправлять). to.have.been.called' не хватит, я не думаю, что я не думаю – CodingIntrigue

ответ

3

Простейшим решением может быть memoize keyDown() как предложено в другом ответе (+1). Вот другой подход, который пытается охватить все основания ...


Поскольку keyDown() импортируется из actions, мы могли бы stub функция для возврата dummy значение всякий раз, когда он вызывается с keyCode:

import * as actions; 
keyDown = stub(actions, "keyDown"); 
keyDown.withArgs(keyCode).returns(dummy); 

Затем наш unit tests подтвердил, что dispatch был вызван с помощью dummy, который мы ранее установили. Мы знаем, что dummy может быть возвращен только нашим обрезанным keyDown(), поэтому эта проверка также подтверждает, что был вызван keyDown().

mapDispatchToProps(dispatch).onKeyDown(createEvent("div", keyCode)); 
expect(dispatch).to.have.been.calledWith(dummy); 
expect(keyDown).to.have.been.calledWithExactly(keyCode); 

Чтобы быть тщательным, мы должны добавить юнит-тесты, чтобы подтвердить, что ключевым событием является не отправляется, когда цель является <input>.

mapDispatchToProps(dispatch).onKeyDown(createEvent("input", keyCode)); 
expect(dispatch).to.not.have.been.called; 
expect(keyDown).to.not.have.been.called; 

Мы должны также test сам keyDown() в изоляции, проверив, что данный dispatch обратного вызова вызывается с правильным ключевым событием и ключом код:

expect(dispatch).to.have.been.calledWith({type: actions.KEYDOWN, key: keyCode}); 

Ссылки

+0

Большое спасибо за ответ. Я не понимал, что импорт ES6 может быть заострен, но это имеет смысл. Хотя другие ответы получили в той же точке, я наградил щедрость здесь за уровень усилий, поставленный в ответ. – CodingIntrigue

+0

@CodingIntrigue Нет проблем :) – tony19

2

keyDown(keycode) каждый раз создает новую функцию, и каждый экземпляр каждой функции различен, тестовый пример завершается неудачно, как и ожидалось.

Это может быть исправлен с помощью ЗАПОМНИТЬ функции, созданных keyDown:

let cacheKeyDown = {}; 
export const keyDown = key => cacheKeyDown[key] || cacheKeyDown[key] = (dispatch, getState) => { 
    const { modifier } = getState().data; 
    dispatch({ type: KEYDOWN, key }); 
    return handle(modifier, key); 
}; 

С запоминанием, keyDown звонков с тем же keycode возвращают ту же функцию.

1

Как @DarkKnight сказал (получил +1), keyDown возвращает новую функцию для каждого вызова, поэтому тест терпит неудачу, потому что keyDown(keyCode)! = keyDown(keyCode).

Если вы не хотите, чтобы изменить фактическую реализацию keyDown, вы можете просто издеваются в тестах:

import * as actions from "./actions"; 

spyOn(actions, 'keyDown'); 

Вы можете увидеть другие ответы о том, как это можно сделать:

 Смежные вопросы

  • Нет связанных вопросов^_^