2013-10-09 4 views
8

Это запах кода для шпионажа на объекте, который тестируется устройством? Например, у меня есть класс LineCounter, задачей которого является просто подсчитать количество строк в строке. -Mockito Spy'ing на проверяемом объекте

class LineCounter { 
    public int getNumLines(String string) { 
     String metadata = getStringMetadata(string); 

     // count lines in file 
     return numLines; 
    } 

    /** Expensive operation */ 
    protected String getStringMetadata(String string) { 
     // do stuff with string 
    } 
} 

Теперь я хочу написать тест JUnit 4 для этого, чтобы проверить метод getNumLines в то время как насмешливое из дорогой getStringMetadata вызова. Я решил использовать механизм шпиона Mockito, чтобы вернуть getStringMetadata фиктивное значение.

class LineCounterTests { 
    @Test public void testGetNumLines() { 
     LineCounter lineCounterSpy = Mockito.spy(new LineCounter()); 

     // Mock out expensive call to return dummy value.    
     Mockito.when(lineCounterSpy.getStringMetadata(Mockito.anyString()).thenReturn("foo"); 

     assertEquals(2, lineCounterSpy.getNumLines("hello\nworld"); 
    } 
} 

Это разумная вещь? Я чувствую себя довольно странно, проверяя объект Spy, а не фактический класс, но я не могу думать о причине против него.

+4

Это может быть случай проверки вождения улучшения вашего кода. Похоже, что задача получения метаданных строк должна быть извлечена в другой класс, который затем делегируется 'LineCounter'. В этот момент вы создали «шов» и можете высмеивать эту зависимость более обычным (и менее вонючим) способом. – millhouse

+0

@millhouse Да, полностью согласен, что это похоже на * правильный способ сделать это. Мне интересно, правда, есть ли что-то по своей сути неправильное в моем примере, где тестируемый объект является шпионом? – Matthew

ответ

3

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

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

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

+0

Предположим, что я проверил 'getStringMetadata' в другом месте. Я все равно хочу протестировать 'getNumLines', не выполняя дорогостоящий вызов' getStringMetadata', так как я уже знаю, что этот метод ведет себя так, как ожидалось. Предложение @ millhouse действительно, и мне нравится подход разделения 'getStringMetadata' в другой класс, но мой первоначальный вопрос все еще остается. Есть что-то * неправильное * с моим примером.Или, может быть, лучший способ задать вопрос: зачем извлекать 'getStringMetadata' в другой класс и высмеивать эту зависимость при проверке' LineCounter' лучшего подхода? – Matthew

+0

Вы также отмечаете, что шпионаж испытуемого класса подвержен рискам. Можете ли вы рассказать об этом? – Matthew

+0

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

0

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

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

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

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