2016-09-09 9 views
1

Мне нужно иметь дело с устаревшим приложением, которое не имеет тестов. Поэтому, прежде чем начинать рефакторинг, я хочу убедиться, что все работает так, как есть.AnyString() как параметр для модульного теста

Теперь представьте себе следующую ситуацию:

public SomeObject doSomething(final OtherObject x, final String something) { 
    if(x == null) throw new RuntimeException("x may not be null!"); 
    ... 
} 

Теперь я хочу, чтобы проверить, что проверка нулевой, поэтому убедитесь, что он работает, и я не потерять его, как только я рефакторинг.

Так что я сделал это

@Test(expected = RuntimeException.class) 
public void ifOtherObjectIsNullExpectRuntimeException() { 
    myTestObject.doSomething(null, "testString"); 
} 

Теперь это работает конечно.

Но вместо «testString» я хотел бы передать случайную строку.

Так что я попытался с:

Но это не допускается, так как я получаю

org.mockito.exceptions.misusing.InvalidUseOfMatchersException:. ... Вы не можете использовать аргумент matchers вне проверки или stubbing

Я понимаю смысл этого, но мне интересно, могу ли я все же сделать то, что хочу, без параметризации моего теста или тому подобного. Единственными библиотеками, которые я могу использовать, являются Junit, AssertJ, Mockito и Powermock.

Любые идеи?

ответ

1

Ну, как Mockito пытается рассказать вам об этом исключении, на самом деле это не так, как вы бы использовали anyString. Такие методы должны использоваться только для издевательств.

Итак, почему бы не попробовать тестирование с помощью случайной строки? Мой личный фаворит в таком сценарии: java.util.UUID.randomUUID().toString(). Это будет практически всегда генерировать новую строку, которая раньше никогда не использовалась для вашего теста.

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

+0

Это работает для строк, но, например, Длинны или другие предметы? – Sorona

+0

Кроме того, randomUUID() никогда не будет возвращать нулевой афайк, так что это главный недостаток. – Sorona

+0

@Sorona См. [Java.util.Random] (https://docs.oracle.com/javase/7/docs/api/java/util/Random.html). Все, что вам нужно для тестирования со случайными значениями, можно найти там. Кроме того, если вы хотите специально протестировать нулевое значение, это должен быть отдельный модульный тест. Модульные тесты должны быть дешевыми и быстрыми; не бойтесь добавить новый тестовый пример, где вы считаете это необходимым! – nasukkin

1

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

2

Испытания должны быть детерминированными. Использование случайных значений в тесте затрудняет воспроизведение поведения при отладке неудавшегося теста. Я предлагаю вам просто создать константу String для теста, например "abcdefg".

+0

Я согласен с тем, что многие тесты должны быть детерминированными, но есть причина, по которой существуют параметризованные тесты. Вы не всегда хотите делать TDD, но BDD также, где тестовый набор пытается использовать всевозможные возможные (и «невозможные») значения, просто чтобы ваши тесты потерпели неудачу. Лично я вижу преимущества обоих взглядов. Если вы идете только на детерминированные тесты, как в «тестах, о которых думал программист», вы не всегда сможете охватить даже самые основные сценарии реального мира. – Sorona

+1

@Sorona Параметрированные тесты, безусловно, могут быть детерминированными. –

+0

Они могут, да, но они не всегда. Ну, на самом деле, если вы обернете голову вокруг рамки и случайности в информатике, они, вероятно, всегда «детерминистские»;) Тем не менее, что я пытаюсь сделать здесь, убедитесь, что независимо от второго параметра он всегда должен терпеть неудачу. Следовательно, я хочу повысить свои шансы, что если какой-то объект придет, он не нарушит мою реализацию. Тесты будут работать 24/7 в принципе, так что статистически ... – Sorona

2

Вы смешиваете концепции здесь.

Все эти помощники «насмешливо», как anyString() предназначены для использования при настройке макет объекта.

Но когда вы проверяете свой код тестирования:

@Test(expected = RuntimeException.class) 
public void ifOtherObjectIsNullExpectRuntimeException() { 
    myTestObject.doSomething(null, "testString"); 
} 

вы найдете: нет абсолютно никаких насмешливый участие в этом тесте. Вы просто не можете использовать эти звонки Mokito в этом месте; потому что «в этом месте нет Мокито».

И как раз для записи - в любом случае, не нужно заходить за борт. Ваша логика здесь очень ясна: когда первый аргумент равен null, вы бросаете это исключение. Таким образом, на самом деле не имеет значения, что входит в качестве второго аргумента. Поэтому, думая в течение часа, как проверить нуль с любым вторым аргументом, хорошо, на мой взгляд: трата вашего времени.

Последняя подсказка: есть java.lang.Objects И этот класс имеет хорошую проверку на нуль, так что мой код продукции только выглядит как

public SomeObject doSomething(final OtherObject x, final String something) { 
    Objects.requireNonNull(otherObject, "otherObject must not be null"); 
    Objects.requireNonNull(something, "something must not be null"); 

Только разницы нет: требует ... броски NullPointerExceptions

Окончательный вариант: некоторые люди предлагают поставить финал на каждый параметр; но я бы этого не сделал. Он добавляет значение в 99% всех случаев. Это просто означает, что у вас больше кода для чтения; без уважительных причин. Но это вопрос стиля.

EDIT на комментарий о том, тест, чтобы проверить наличие возможных изменений в будущем: вы не должны делать это:

  1. В определенной степени, как проверяется ваш вклад деталь реализации. Вы не проверяете детали реализации. Другими словами:
  2. Ваш метод имеет определенный контракт (который вы, например, указываете неофициально, написав javadoc, который говорит, что «вызывает NPE на нулевом вводе»). Ваши тесты должны подтвердить точно, что текущий договор. И контракт заключается в следующем: выбрасывает, если первый аргумент равен null.

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

+0

Дело в том, что я согласен с вами. Но действительно случилось, что люди изменили код так, что он выбрасывал исключение 'if (a &&! B)' вместо 'if (a)' ... Я хотел бы получить тест, который гарантирует, что только первый аргумент используется в этой проверке. – Sorona