Spring + QueryDsl + Mockito: Как написать пример модульного теста для простой функции - PullRequest
0 голосов
/ 10 января 2019

У меня есть следующая функция в моем сервисе.

public boolean checkNameUnique(String name) {
    QEntity qEntity = QEntity.entity;
    BooleanExpression nameUniquePredicate = qEntity.name.eq(name);
    long count = entityReadRepository.count(nameUniquePredicate);
    return count == 0;
}

Он просто проверяет, существует ли имя в БД. Он должен быть уникальным, поэтому он возвращает true, если он еще не существует, и false, если он существует.

Теперь, как мне написать для этого тестовый блок mockito? Я новичок в Mockito и пишу кейсы, поэтому вопрос.

Мое чтение на Мокито заставило меня написать что-то вроде

when(entityReadRepository.count(nameUniquePredicate)).thenReturn(1);

и затем вызовите функцию для проверки. Но это не имеет никакого смысла.

Entity - это объект Hibernate, соответствующий таблице в БД entityReadRepository расширяется JpaRepository и QueryDslPredicateExecutor. QEntity - это объект Q, сгенерированный плагином QueryDsl.

1 Ответ

0 голосов
/ 15 января 2019

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

when(entityReadRepository.count(any(BooleanExpression.class))).thenReturn(1l);

Ваша проблема в том, что в вашем методе много статических вызовов и объектов, которые не могут быть обработаны изящно. Одним из вариантов является использование таких инструментов, как Powermockito, где вы можете смоделировать поведение статических методов. Если вы предпочитаете использовать mockito, вы можете извлечь статический фрагмент кода в отдельный метод и создать шпиона тестируемого класса:

package com.slavpilus;

    import org.junit.Assert;
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    import org.mockito.Spy;
    import org.mockito.runners.MockitoJUnitRunner;

    import static org.mockito.Matchers.any;
    import static org.mockito.Mockito.doReturn;
    import static org.mockito.Mockito.when;


    @RunWith(MockitoJUnitRunner.class)
    public class TPresenterTest {

        @InjectMocks
        @Spy
        private ClassUnderTest target = new ClassUnderTest();

        @Before
        public void setUp() {
            doReturn(null).when(target).getUniqueNamePredicate();
        }

        @Mock
        private YourRepositoryDependency entityReadRepository;

        @Test
        public void checkNameUniqueShouldBeTrueIfNameNotInDatabase() {
            when(entityReadRepository.count(any())).thenReturn(0l);

            boolean isUnique = target.checkNameUnique("anyName");

            Assert.assertTrue(isUnique);
        }

        @Test
        public void checkNameUniqueShouldBeFalseIfNameFoundInDatabase() {
            when(entityReadRepository.count(any())).thenReturn(1l);

            boolean isUnique = target.checkNameUnique("anyName");

            Assert.assertFalse(isUnique);
        }
    }

и ваш производственный код будет выглядеть примерно так:

   public boolean checkNameUnique(String name) {
        BooleanExpression nameUniquePredicate = getUniqueNamePredicate();

        long count = entityReadRepository.count(nameUniquePredicate);
        return count == 0;
    }

    protected BooleanExpression getUniqueNamePredicate() {
        QEntity qEntity = QEntity.entity;
        return qEntity.name.eq(name);
    }

Однако этот подход оставляет некоторый код непроверенным, так как метод getUniqueNamePredicate полностью пропускается во время выполнения теста.

...