Модульный тест для нескольких условий в Java?

Я новичок в модульном тестировании, и иногда у меня бывают такие ситуации с несколькими условиями. Однако я не уверен, буду ли я повторно издеваться или проверять одни и те же случаи для каждого теста.

Например, я пытаюсь написать модульные тесты для следующего метода службы:

public void create(Request request) {
    
    // code omitted

    if (!employeeService.existsByUuid(uuid)) {
        throw new EntityNotFoundException("Not found");
    }

    EmployeeDTO employee = employeeService.save(...);
    
    if (!departmentService.existsByUuid(employee.getDepartment())) {
        throw new EntityNotFoundException("Not found");
    }
}

Я думаю, что мне нужно написать свои тесты для следующих сценариев:

1., когда employeeService.existsByUuid(uuid) == false, затем создайте новое исключение EntityNotFoundException. затем убедитесь, что employeeService.save() и departmentService.existsByUuid() никогда не вызываются.

2. когда employeeService.existsByUuid(uuid) == true вызывается employeeService.save() и я утверждаю значения. а затем убедитесь, что employeeService.save() и departmentService.existsByUuid() никогда не вызываются.

3., когда departmentService.existsByUuid() == false, затем создайте новое исключение EntityNotFoundException. На этом этапе я также имитирую employeeService.existsByUuid(uuid) как true, чтобы тест прошел первое условие. Однако я не уверен, нужно ли мне утверждать вторую часть; employeeService.save() вызывается, и я утверждаю ценности. Утверждаю ли я возвращаемые значения или просто проверяю, что метод вызывается 1 раз. Потому что я уже утверждал его значение, а 3-й тест как раз для 3-го условия.

Любая идея для такого рода сценариев, когда у нас есть несколько условий и может потребоваться повторное тестирование одного и того же условия снова и снова?

В идеале вы должны написать один отдельный тест для каждого отдельного сценария. Для вещей, которые необходимо настраивать каждый раз, вы можете использовать @Before в своем тестовом классе.

QBrute 16.03.2022 12:43

@QBrute Спасибо, но я имею в виду не общие вещи, которые можно собрать в разделе @Before. Итак, вы имеете в виду, что мне нужно выполнять утверждения для части employeeService.save(), даже если я проверяю первое или третье условие? Или просто проверьте, называется он или нет в зависимости от ситуации. Есть идеи?

Ashley 16.03.2022 12:51
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
2
63
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

В вашем примере я думаю, что это могут быть основные случаи:

  1. если UUID не существует, выбрасывается исключение и сотрудник не сохраняется
  2. если UUID существует, все поля сотрудника сохраняются корректно
  3. если сотрудник сохранен, но отдел сотрудника не существует, выдается исключение

Чтобы проверить их, вы можете сделать что-то вроде этого:

EmployeeService employeeService = mock(EmployeeService.class);

Дело 1:

when(employeeService.existsByUuid(employeeUuid)).thenReturn(false);   
try {
    testObject.create(request);
    fail();
}
catch(EntityNotFoundException e) {
    verify(employeeService, never()).save(...);
}

случай 2:

when(employeeService.existsByUuid(employeeUuid)).thenReturn(true); 
when(employeeService.existsByUuid(departmentUuid)).thenReturn(true);    
testObject.create(request);
verify(employeeService).save(field1, field2, ...);

случай 3:

when(employeeService.existsByUuid(employeeUuid)).thenReturn(true);   
when(employeeService.existsByUuid(departmentUuid)).thenReturn(false);   
try {
    testObject.create(request);
    fail();
}
catch(EntityNotFoundException e) {
    // success
}

Кстати, вы также можете указать ожидаемые исключения в аннотации @Test, но тогда вы не сможете выполнять дальнейшую проверку результатов:

@Test(expected = EntityNotFoundException.class)
public void test3() {
    when(employeeService.existsByUuid(employeeUuid)).thenReturn(true);   
    when(employeeService.existsByUuid(departmentUuid)).thenReturn(false);   
    testObject.create(request);
}

Вы имеете в виду, что я НЕ нужно выполняю утверждения для части employeeService.save(), когда я проверяю первое или третье условие, и просто проверяю, вызывается ли employeeService.save() или нет (в соответствии с условием), когда я проверяю первое или третье условие. Есть идеи?

Ashley 16.03.2022 12:55

Я думаю, что для первого случая важным требованием является то, чтобы сотрудник НЕ был спасен. Во втором случае следует проверить, что сотрудник сохранен ПРАВИЛЬНО. А для третьего случая экономия не актуальна.

Adriaan Koster 16.03.2022 12:59

Большое спасибо, сначала я не использую try-catch, а просто использую нотацию JUnit5 для тестирования исключений. Пункты, в которых я не уверен, перечислены ниже, не могли бы вы разъяснить мне один за другим?

Ashley 16.03.2022 13:14
1. Когда я проверяю первое условие, я обычно предпочитаю использовать проверку для оставшихся вызовов метода. Это плохо? Или, за исключением случаев тестирования, я должен пропустить проверку вызовов методов после выполнения моего случая?
Ashley 16.03.2022 13:16
2., когда я тестирую часть, должен ли я просто имитировать необходимую часть и не нужно проверять другие части (кейсы)?
Ashley 16.03.2022 13:21

1. Проверка неплохая, но иногда и не нужная. Например, в случае 1, employeeService заглушен, чтобы вернуть «false», и мы ожидаем, что код выдаст исключение. Если происходит исключение, поведение соответствует ожидаемому. Никаких дополнительных проверок там делать не нужно. Но для действия сохранения мы хотим явно убедиться, что оно НЕ вызывается, поэтому нам нужно это проверить.

Adriaan Koster 16.03.2022 14:10

2. Старайтесь, чтобы каждый тестовый пример был как можно более целенаправленным (при этом сохраняя его удобочитаемость). Нет необходимости повторять утверждения или проверки, которые не нужны для этого конкретного случая.

Adriaan Koster 16.03.2022 14:11

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

Adriaan Koster 16.03.2022 14:12

Что касается try-catch, то он нужен в случае 1, потому что вы хотите что-то проверить ПОСЛЕ того, как было выброшено исключение. Если вы используете аннотацию, то никаких дополнительных проверок, как я уже упоминал, выполнять нельзя. Для случая 3 можно обойтись без try-catch.

Adriaan Koster 16.03.2022 14:13

Я использую ArgumentCaptor, но много публикую здесь, чтобы упростить код. Большое спасибо, амиго, очень полезный ответ и комментарии.

Ashley 16.03.2022 14:40

Любая помощь о Эта проблема?

Ashley 17.03.2022 08:12

Есть комментарий, в котором говорится то же самое, что я упомянул: «вам нужно поймать исключение и выполнить утверждения после» (stackoverflow.com/questions/48746002/…)

Adriaan Koster 18.03.2022 12:19

Спасибо, но я не предпочитаю использовать try-catch и думаю, что может быть другая альтернатива. Есть идеи?

Ashley 18.03.2022 12:37

Вы можете использовать assertThrows, как объяснил HariHaravelan

Adriaan Koster 18.03.2022 15:06

Вы также можете использовать правило ExpectedException. Обзор см. в этой статье: blog.aspiresys.pl/технологии/….

Adriaan Koster 18.03.2022 15:12
Ответ принят как подходящий

Вы можете использовать mockito для проверки и утверждения бросков, чтобы проверить свои цели, как показано ниже.

    @Test
    public void testOne(){
        when(employeeService.existsByUuid(uuid)).thenReturn(false);

        assertThrows(EntityNotFoundException.class, () -> {
            create(request);
        });

        verify(employeeService, times(0)).save(eq(empObj));
        verify(departmentService, times(0)).existsByUuid(eq(departmentObj));
    }




    @Test
    public void testTwo(){
        when(employeeService.existsByUuid(uuid)).thenReturn(true);
         when(departmentService.existsByUuid(uuid)).thenReturn(true);

        create(request);

        verify(employeeService, times(1)).save(eq(empObj));
        verify(departmentService, times(1)).existsByUuid(eq(departmentObj));
    }

    @Test
    public void testThree(){
        when(employeeService.existsByUuid(uuid)).thenReturn(true);
        when(departmentService.existsByUuid(uuid)).thenReturn(false);

        assertThrows(EntityNotFoundException.class, () -> {
            create(request);
        });

        verify(employeeService, times(1)).save(eq(empObj));
        verify(departmentService, times(1)).existsByUuid(eq(departmentObj));
    }

Другие вопросы по теме