#java #юнит_тесты #junit
Я вижу в этом паттерне только один плюс: он улучшает читаемость. А минусов больше: Во-первых, это далеко не всегда улучшает читаемость. Вот простой тест: @Test public void testAdditionAndSize() { int expectedSize = 3; int actualSize = list.size(); assertEquals(expectedSize, actualSize); } Вместо этого я мог бы написать так: @Test public void testAdditionAndSize() { assertEquals(3, list.size()); } Разве пострадала читаемость? Во-вторых, что делать вот с таким тестом? @Test public void testIsEmpty() { assertTrue(!list.isEmpty()); } Неужели тоже создавать 2 булевы переменные, а потом сравнивать их? @Test public void testIsEmpty() { boolean res = false; boolean actual = list.isEmpty(); assertEquals(res, actual); } И в-третьих, На мой взгляд, этот паттерн усложняет написание некоторых тестов. Например: @Test public void testCicleNext() { assertEquals(8, list.next().getId()); assertEquals(7, list.next().getId()); assertEquals(9, list.next().getId()); assertFalse(list.hasNext()); assertEquals(8, list.next().getId()); assertTrue(list.hasNext()); } Тут перед каждым ассертом придется создавать по 2 переменные. В результате тест разрастется до огромных размеров и читать его по-моему станет только тяжелее.
Ответы
Ответ 1
В любом паттерне всегда можно найти такой случай, когда он выглядит нелепо. Я думаю, что Вы как раз и привели такой пример. Классические обоснования паттерна 3А (да, так он пишеться - три А) следующие: код разделяется на три логические части. функция тестирования четко тестирует две вещи, без побочных эффектов. разберем первый тест: assertEquals(3, list.size()); Здесь не всегда понятно, есть ли у функции size побочные эффекты (да, в этом случае это звучит глупо, но в общем случае - нет). И допустим этот тест захотелось закомментировать временно. А код в тесте ниже может неявно зависеть от побочных эффектов. И все, прилетели... (да, в этом конкретном тесте одна строка, но кто его знает) Во-вторых, что делать вот с таким тестом? @Test public void testIsEmpty() { assertTrue(!list.isEmpty()); } А этот тест просто нужно правильно написать. Где то так @Test public void testListIsNotEmpty() { bool is_empty = list.isEmpty(); assertFalse(is_empty); } Видите разницу? нет переменных типа res, значение которых не понятно. Также, если список не должен быть пуст, то это и нужно тестить, а не противоположность (видите, я даже имя теста поменял). А теперь посмотрим на последний тест @Test public void testCicleNext() { assertEquals(8, list.next().getId()); assertEquals(7, list.next().getId()); assertEquals(9, list.next().getId()); assertFalse(list.hasNext()); assertEquals(8, list.next().getId()); assertTrue(list.hasNext()); } В этом примере next модифицирует исходный объект. И если одну строку закомментировать, то все, тест может порушиться. Этот тест кстати можно переписать через цикл и сразу он стает красивее и проще. И не нужно создавать кучу переменных. @Test public void testCicleNext() { int test_data[] = {8,7,9,8}; for (int i = 0; i < test_data.length(); i++) { int expect = test_data[i]; int actual = list.next().getId(); bool has_next = list.hasNext(); assertEquals(expect, actual); assertTrue(has_next ); } } Теперь, если нужно добавить ещё 5-6 тестируемых значений, то это очень просто. Мое личное мнение. Внутри тестируемых функций (то есть, assertEquals и подобных) не должно быть сложных конструкций. На то они и юнит тесты, что бы тестировать минимальную единицу кода. И если size еще как то допустим, то методы, которые модифицируют объект - нет. Если у меня возникает ситуация, что мне нужно продебажить функцию/метод, который я тестирую, то это первый звонок, что функция сложная и ее нужно разбить или переписать. Если тест стает очень большим (а Вы на это жалуетесь), значит тест написан неверно - его нужно либо разбить на два поменьше, либо написать правильно (я, к примеру в плюсах люблю для этого делать макросы:) ). Но в любом случае не нужно страдать паттернами головного мозга и применять их везде и всюду. Нужно применять их по надобности. я давно не писал на Java, поэтому в коде могут быть мелкие опечатки.Ответ 2
Arrange-Act-Assert - это шаблон для форматирования Unit тестов. Обозначающий разделения теста на 3 части Arrange - все необходимые подготовки и входные данные Act - собственно вызов того метода который вы тестируете Assert - проверки, что метод делает то что надо В ваших пример со списком должно быть что-то такое: @Test public void testAdditionAndSize() { // Arrange - подготавливаем данные для теста SomeClass obj = new SomeClass; // Act - выполняем метод add list.add(obj); // Assert - проверяем, что объект добавился assertEquals(1, list.size()); assertEquals(obj, list.get(1)); // другие тесты проверяющие что добавился правильный объект, что он не изменился и т.д. }
Комментариев нет:
Отправить комментарий