2014-10-21 3 views
1

Мой метод возвращает исключение, если входной String (StringBuilder в моем случае) длиннее 2048 символов. Поскольку мы не можем высмеивать финальные классы с помощью Mockito, может кто-нибудь подскажет мне, как это сделать.Mockito: протестируйте метод, который принимает строку длинной длины

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

+0

Я не понимаю проблему, не могли бы вы объяснить немного больше или написать какой-нибудь код? Если вы пытаетесь бросить и исключить и проверить его, вы должны добавить параметр в аннотацию @Test (expected = YourException.class) –

+0

Почему передача строки глупа? Я видел слишком много злоупотреблений макетом. –

+0

Для записи есть аналогичный вопрос [Как издеваться над String using mockito?] (Http://stackoverflow.com/q/1079239/1426891) уже на StackOverflow. Я не думаю, что это дубликат, хотя этот вопрос касается насмешливой стратегии, а не возможностей библиотеки. –

ответ

1

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


Вы правильно заметили, что строка является final, поэтому Mockito не может использовать прокси или поколение байткодом молча создать подкласс для него. Вместо этого вы должны были бы использовать Powermock, что на самом деле переписать байт-код вашего класса-под-тестом, используя пользовательский загрузчик классов, чтобы использовать ваш mocked String.length() вместо фактической реализации. В любом случае, это очень много работы, чтобы имитировать простую строку, и, возможно, это займет больше времени и памяти теста, чем альтернатива.

Даже если String не был final, вы все равно будете писать хрупкий тест, потому что вы (предположительно) только издеваетесь над подмножеством String. В этом гипотетическом, вы могли бы написать:

when(mockString.length()).thenReturn(2048); 

, но потом, в один прекрасный день, ваша реализация превращается в:

public boolean isStringValid(String input) { 
    // Handle Unicode surrogate pairs. 
    return input.codePointCount(0, input.length()) <= 1024; // NOT 2048 
} 

... и вдруг ваш тест ошибочно проходит, потому что Mockito видит unmocked codePointCount и возвращается его значение по умолчанию int. 0. Это одна из причин того, что тесты с использованием неполных mocks менее устойчивы и менее ценны, чем тесты с использованием реальных объектов.

Но как насчет полных издевательств? Тогда вы могли бы высмеять codePointCount и так далее. Взятый к его логическому завершению, вы могли представить себе объект смешного String, который настолько продвинут, он правильно возвращает значение для абсолютно любого вызова, который вы ему даете. В этот момент вы переопределили строку изнутри, вероятно, за счет удобочитаемости, и потратили много инженерных часов, воспроизводя одну из самых проверенных, наиболее очевидных реализаций в истории Java по очень небольшой причине. Если что-нибудь, , что кажется довольно глупым для меня.

Несколько советов:

  • Не смейся реализации вы не владеете. Mockito по-прежнему привязан к отметкам final в реализациях, которые он издевается, например, так насмехается над другими типами - это плохая привычка. Когда детали реализации других команд могут нарушить ваши тесты, это очень плохой знак.

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

  • В частности, не издевайтесь над объектами данных. Например, они полагаются на getFoo, соответствующие setFoo, а также могут рассчитывать на equals и hashCode.В любом случае хорошо продуманные объекты данных не имеют внешних зависимостей.

  • Mock интерфейсы вместо классов реализации, когда вы можете. Это изолирует вас от деталей реализации, которые изменяют работу ваших макетов.

  • Если вы обнаружите, что объект данных, который вызывает служебные вызовы или использует ресурсы, которые не подходят для тестирования, помните, что вы можете реорганизовать классы для повышения тестовой способности, например, определенные классы или извлечение интерфейсов и сделать custom reusable test doubles. Ваши тесты должны быть первоклассными пользователями ваших классов, и ваша прерогатива изменяет классы, которые вы контролируете (или обертываете классы, которые вы не контролируете) для проверки. Я часто обнаружил, что это упрощает использование этих классов в производстве.

+0

Я согласен со всеми указанными вами моментами. Что делать, если лимит символов изменяется с 2048 до 20K или может быть еще больше. В этом случае вы по-прежнему рекомендуете создавать настоящую строку? – Sachin

+1

** Да, без второй мысли. ** Используйте реальную строку для любого случая, когда система-под-тест может найти настоящую строку в wild-2k, 20k, 2M. Гораздо больше этого, и String может быть не правильным типом данных при тестировании или производстве, потому что Strings - это большие непрерывные неизменные блоки памяти; Я бы дважды проверил свой дизайн и подумал о переходе на CharSequence (маленький и макетируемый интерфейс), а затем с помощью своего рода [Rope] (http://en.wikipedia.org/wiki/Rope_ (data_structure)). NB: CharSequence предоставляет 'toString', так или иначе. :) –

0

ИМХО, нет необходимости в Mockito здесь, просто использовать то, что создает известную длину String (здесь я использую Викисклад Ланг RandomStringUtils). Я также использую ExpectedException для проверки на исключения

public class MyClassTest { 
    private static int LIMIT = 2048; 
    private static String TEST_STRING1 = RandomStringUtils.random(LIMIT); 
    private static String TEST_STRING2 = RandomStringUtils.random(LIMIT + 1); 

    @Rule 
    public ExpectedException ee = ExpectedException.none(); 

    private MyClass myClass = new MyClass(); 

    @Test 
    public void smallStringShouldBeOk() { 
     myClass.myMethod("foobar"); // OK if no exception or assert on a returned value 
    } 

    @Test 
    public void edgeCaseStringShouldThrow() { 
     ee.expect(SomeException.class) 
     ee.expectMessage("some message"); 

     myClass.myMethod(TEST_STRING1); 
    } 

    @Test 
    public void tooLongStringShouldThrow() { 
     ee.expect(SomeException.class) 
     ee.expectMessage("some message"); 

     myClass.myMethod(TEST_STRING2); 
    } 
}