【问题标题】:Spring + QueryDsl + Mockito: How to write a unit test case for a simple functionSpring + QueryDsl + Mockito:如何为一个简单的函数编写单元测试用例
【发布时间】:2019-01-10 15:02:04
【问题描述】:

我的服务中有以下功能。

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 实体,对应于 DB 中的一个表 entityReadRepository 扩展了 JpaRepositoryQueryDslPredicateExecutorQEntityQueryDsl 的插件生成的Q 对象。

【问题讨论】:

  • 我正在考虑是否可以模拟将对象插入到 repo 中,然后 checkNameUnique 会自动选择存在的某某对象。
  • “但这没有任何意义”你为什么这么说?你的模拟代码对我来说是正确的。如果这是一个单元测试,你不应该测试你的持久层,你不应该测试你的数据库层是否在做它应该做的事情。
  • 我这么说是因为我只是重写了返回 1 的语句。这是测试该代码的有效方法吗?还是这个函数非常简单并且依赖于数据库,以至于模拟单元测试用例在这里没有意义?
  • 另外,我正在寻找一些解决方案,我可以说在模拟仓库中插入一个模拟对象,然后当我调用该函数时,它应该搜索模拟仓库并给出答案?这在 mockito 中是否可行?
  • 在阅读更多内容后,我看到人们使用实体管理器并尝试这样做。但这对我不起作用。我没有使用任何实体管理器。将其全部抽象为 querydsl 和 jparepository 扩展

标签: spring hibernate jpa mockito querydsl


【解决方案1】:

单元测试通常会模拟任何外部依赖项,在您的情况下为 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 方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    相关资源
    最近更新 更多