Намерение
Я ищу следующее:
- Твердая модульная методика тестирования
- Что мне не хватает в моем подходе?
- Что я делаю неправильно?
- Что я делаю, что не нужно?
- Способ получения максимально возможного значения автоматически
Текущая среда
- Eclipse как IDE
- JUnit как среда тестирования, интегрированная в Eclipse
- Hamcrest как библиотека "matchers", для лучшей читаемости утверждения
- Google Guava для проверки предварительных условий
Текущий подход
Структура
- Один тестовый класс для каждого класса для тестирования
- Тестирование методов сгруппировано в статические вложенные классы
- Именование метода метода для определения тестируемого поведения + ожидаемый результат
- Ожидаемые исключения, указанные Java Annotation, а не в имени метода
Методология
- Следите за значениями
null
- Следите за пустым List <E>
- Следите за пустым String
- Остерегайтесь пустых массивов
- Остерегайтесь инвариантов состояния объекта, измененных кодом (пост-условия)
- Способы принимают документированные типы параметров
- Граничные проверки (например, Integer.MAX_VALUE и т.д.)
- Документирование неизменяемости по определенным типам (например, Google Guava ImmutableList <E> )
- ... есть ли список для этого? Примеры хороших тестовых списков:
- Вещи для проверки проектов базы данных (например, CRUD, подключение, ведение журнала,...)
- Что нужно проверить в многопоточном коде.
- Что нужно проверить для EJB
- ...?
Пример кода
Это надуманный пример, чтобы показать некоторые методы.
MyPath.java
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Arrays;
import com.google.common.collect.ImmutableList;
public class MyPath {
public static final MyPath ROOT = MyPath.ofComponents("ROOT");
public static final String SEPARATOR = "/";
public static MyPath ofComponents(String... components) {
checkNotNull(components);
checkArgument(components.length > 0);
checkArgument(!Arrays.asList(components).contains(""));
return new MyPath(components);
}
private final ImmutableList<String> components;
private MyPath(String[] components) {
this.components = ImmutableList.copyOf(components);
}
public ImmutableList<String> getComponents() {
return components;
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
for (String pathComponent : components) {
stringBuilder.append("/" + pathComponent);
}
return stringBuilder.toString();
}
}
MyPathTests.java
import static org.hamcrest.Matchers.is;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.hamcrest.collection.IsEmptyCollection.empty;
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.hamcrest.core.IsNot.not;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import com.google.common.base.Joiner;
@RunWith(Enclosed.class)
public class MyPathTests {
public static class GetComponents {
@Test
public void componentsCorrespondToFactoryArguments() {
String[] components = { "Test1", "Test2", "Test3" };
MyPath myPath = MyPath.ofComponents(components);
assertThat(myPath.getComponents(), contains(components));
}
}
public static class OfComponents {
@Test
public void acceptsArrayOfComponents() {
MyPath.ofComponents("Test1", "Test2", "Test3");
}
@Test
public void acceptsSingleComponent() {
MyPath.ofComponents("Test1");
}
@Test(expected = IllegalArgumentException.class)
public void emptyStringVarArgsThrows() {
MyPath.ofComponents(new String[] { });
}
@Test(expected = NullPointerException.class)
public void nullStringVarArgsThrows() {
MyPath.ofComponents((String[]) null);
}
@Test(expected = IllegalArgumentException.class)
public void rejectsInterspersedEmptyComponents() {
MyPath.ofComponents("Test1", "", "Test2");
}
@Test(expected = IllegalArgumentException.class)
public void rejectsSingleEmptyComponent() {
MyPath.ofComponents("");
}
@Test
public void returnsNotNullValue() {
assertThat(MyPath.ofComponents("Test"), is(notNullValue()));
}
}
public static class Root {
@Test
public void hasComponents() {
assertThat(MyPath.ROOT.getComponents(), is(not(empty())));
}
@Test
public void hasExactlyOneComponent() {
assertThat(MyPath.ROOT.getComponents(), hasSize(1));
}
@Test
public void hasExactlyOneInboxComponent() {
assertThat(MyPath.ROOT.getComponents(), contains("ROOT"));
}
@Test
public void isNotNull() {
assertThat(MyPath.ROOT, is(notNullValue()));
}
@Test
public void toStringIsSlashSeparatedAbsolutePathToInbox() {
assertThat(MyPath.ROOT.toString(), is(equalTo("/ROOT")));
}
}
public static class ToString {
@Test
public void toStringIsSlashSeparatedPathOfComponents() {
String[] components = { "Test1", "Test2", "Test3" };
String expectedPath =
MyPath.SEPARATOR + Joiner.on(MyPath.SEPARATOR).join(components);
assertThat(MyPath.ofComponents(components).toString(),
is(equalTo(expectedPath)));
}
}
@Test
public void testPathCreationFromComponents() {
String[] pathComponentArguments = new String[] { "One", "Two", "Three" };
MyPath myPath = MyPath.ofComponents(pathComponentArguments);
assertThat(myPath.getComponents(), contains(pathComponentArguments));
}
}
Вопрос, явно сформулированный
-
Есть ли список методов, используемых для построения unit test? Что-то гораздо более продвинутое, чем приведенный выше упрощенный список (например, проверка нулей, проверка границ, проверка ожидаемых исключений и т.д.), Возможно, доступная в книге для покупки или URL-адресе для посещения?
-
Как только у меня есть метод, который принимает определенный тип параметров, могу ли я получить плагин Eclipse чтобы создать для меня тесты для моих тестов? Возможно, используя Java Annotation, чтобы указать метаданные о методе и иметь средство для материализации соответствующих проверок для меня? (например, @MustBeLowerCase, @ShouldBeOfSize (n = 3),...)
Мне кажется утомительным и роботоподобным, что нужно помнить обо всех этих "трюках QA" и/или применять их, я нахожу его склонным к ошибкам копировать и вставлять, и я нахожу его не самодокументирующим, когда я кодирую вещи как я делаю выше. По общему признанию, Hamcrest libraries идет в общем направлении специализированных типов тестов (например, на String с использованием RegEx, на объектах File и т.д.), но, очевидно, не автогенерируют любые тестовые заглушки и не задумывайтесь над кодом и его свойствами и подготовьте для меня проводку.
Помогите мне сделать это лучше, пожалуйста.
PS
Не говорите мне, что я просто представляю код, который является глупым оберткой вокруг концепции создания пути из списка шагов пути, предоставляемых статическим методом factory, пожалуйста, это полностью выдуманный пример, но он показывает "несколько" случаев проверки аргументов... Если бы я включил гораздо более длинный пример, кто бы действительно прочитал этот пост?