2016-12-19 2 views
0

Я пытаюсь создать приложение реакции/сокращения, которое не является стандартным «todo». Поэтому я пошел с 2048, это довольно забавная игра, с которой я мог бы поиграть.Как провести тестирование редуктора с гарантированной случайностью? (Redux)

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

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

const Board = (state = 0, action) => { 
    if(state === 0){ 
     return createBoard(); 
    } 
    switch (action.type) { 
     case 'MOVE_UP': 
      return move(state, 0); 
     case 'MOVE_RIGHT': 
      return move(state, 1); 
     case 'MOVE_DOWN': 
      return move(state, 2); 
     case 'MOVE_LEFT': 
      return move(state, 3); 
     case 'NEW_GAME': 
      return createBoard(); 
     default: 
      return state; 
    } 
} 

Однако движение (доска, направление) функция делает большую часть тяжелой и здесь возникает проблема:

function move(board, direction){ 
    //0: up, 1: right, 2: down, 3: left 
    var b; 
    switch(direction){ 
     case 0: 
      b = mergeBoardUp(board); 
      break; 
     case 1: 
      b = mergeBoardRight(board); 
      break; 
     case 2: 
      b = mergeBoardDown(board); 
      break; 
     case 3: 
      b = mergeBoardLeft(board); 
      break; 
     default: 
      b = board.map(x => x); 
    } 
    if(distinctBoard(board, b)){ 
     b = addNewTile(b); 
    } 
    return b; 
} 

Мое понимание заключается в том, что редукторы должны быть проверены на «черный ящик» (не пытайтесь вытащить и проверить функцию «mergeBoardUp», проверьте состояние после его использования). Из-за характера игры новая плитка добавляется всякий раз, когда воспроизводится действительный ход (if(distinctBoard){addNewTile}). У меня есть довольно хороший набор тестовых примеров, которые я бы хотел реализовать с точки зрения тестирования вращений и слияния плат, однако я не могу найти способ обойти «случайное» размещение плитки.

пример тест для справки:

it("MoveUp - simple",() => { 
    var input = [[2,0,0,0], 
       [2,4,8,16], 
       [2,4,0,16], 
       [2,4,8,0]]; 

    var inputHistory = { 
     past: [], 
     present: input, 
     future: [] 
    }; 

    var expected = [[4,8,16,32], 
        [4,4,0,0], 
        [0,0,0,0], 
        [0,0,0,0]]; 

    var rotatedLeft = Board(inputHistory, MovementButtons.moveUp()).present; 

    expect(rotatedLeft).toBe(expected); 
}) 

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

Каким будет подходящий подход для тестирования в этом сценарии? Мои инстинкты кричат ​​на меня, чтобы отделить функции, которые я хотел бы проверить в файле утилиты (и проверить экспортированные функции), но это похоже на анти-шаблон для тестирования редукторов.

ответ

3

Настоящая проблема заключается в том, что случайность не должна быть в редукторе. Редукторы должны быть «чистыми функциями», без побочных эффектов. Код редуктора, который не является чистым, по-прежнему будет работать, но отладка во время путешествия не будет работать должным образом, и, как вы обнаружили, тестирование становится затруднительным.

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

+1

Я хотел бы убедиться, что я правильно понимаю это. Вместо того, чтобы иметь ручку редуктора в случайном порядке, должно ли actionCreator отправить «случайную» плиту, которую нужно заполнить, а также сдвинуть плату? Похоже, это решает мое недоразумение! Тогда я мог бы проверить смещение с помощью набора «случайных» плит! Очень признателен! – Hodrobond

+0

Да, я думаю, что создатель действия мог бы определить случайную плитку, _or_ просто генерировать случайное число или семя, которое затем использовал бы редуктор, чтобы принять решение. Таким образом, сам редуктор детерминирован. – markerikson

0

Я бы спросил, что я пытаюсь проверить? Похоже, вы можете тестировать две разные функции: слияние MoveUp и случайную новую строку MoveUp.

Похоже, что вы могли правильно модульное тестирование функциональности Объединить MoveUp просто с:

expect(rotatedLeft.slice(0,3)).toEqual(expected.slice(0,3)); 

При правильном захвате слияния трех верхних строк, я бы сказал, что это достаточно в качестве модульного тестирования для объединения MoveUp в функциональность.

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

for (var i = 0; i < rotatedLeft[3].length; i += 1) { 
     expect([0, 2, 4]).toContain(rotatedLeft[3][i]); 
    } 
+0

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

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