如何在服务层单元测试中模拟数据库结果?

Posted

技术标签:

【中文标题】如何在服务层单元测试中模拟数据库结果?【英文标题】:How to mock database results in service layer unit test? 【发布时间】:2019-12-21 23:07:36 【问题描述】:

我是在单元测试中模拟事物的新手,我已经开始尝试使用 Mockito。是否可以使用 Mockito?

我正在尝试为分层 Web 服务应用程序编写单元测试。此时我正在测试Service层,它调用DAO层从数据库中获取数据。 我需要模拟数据库结果,这样我就不会在每次运行单元测试时都对数据库进行真正的更新调用。

我想通过模拟我的服务层调用的 DAO 对象来做到这一点,但我需要在不更改源代码的情况下这样做。如果我目前正在对服务层进行单元测试,如何让它使用模拟的 DAO 层?

我的单元测试如下所示:

@Test
public void testUpdate() 
RequestObject request = new RequestObject();
request.setEntityId(1234);
request.setLob('testLOB');
ResponseObject response = service.updateMember(request);

我的服务方法:

public ResponseObject updateMember(RequestObject request) 
ResponseObject result = DAO.updateMember(request);

在这种情况下是否可以在不模拟服务的情况下模拟 DAO? 编辑:我正在使用 Maven 进行依赖管理。

【问题讨论】:

你使用什么依赖管理系统? (Maven/Gradle)您能否将您的依赖项添加到您的问题中,这会对可能给出的答案产生影响。 在服务到测试中模拟 DAO 的难度取决于您如何将模拟的 DAO 注入到服务中。例如是否服务的构造函数允许传入一个 DAO 实例,或者一个可以创建模拟 DAO 的工厂? 【参考方案1】:

您需要在测试设置中使用 Mokito 之类的 Mocker。比如:

public class YouTestClass 

    @MockBean
    DAO mockedDAO;
...
@Test
public void testUpdate() 
        Mockito.when(mockedDAO.updateMember(Mockito.isA(RequestObject.class)).thenReturn(new ResponseObject());
RequestObject request = new RequestObject();
request.setEntityId(1234);
request.setLob('testLOB');
ResponseObject response = service.updateMember(request);

您需要将.thenReturn(new ResponseObject()); 调整为您实际希望收到的用于测试的ResponseObject。此外,您的 DAO 类似乎是一个静态类,因此您可能需要阅读如何在可能的情况下模拟它。否则,您可能希望将其设为单例。

【讨论】:

这里让我感到困惑的是 Mockito.when 方法。这不只是告诉 Mockit 在调用服务方法时始终返回指定的值吗?我想指定 DAO 返回的内容,而不是服务。【参考方案2】:

例如,如果您在服务层上进行测试,您可以这样做。

@Mock
private DAO dao;

@Test
public void testUpdate() 

   RequestObject request = new RequestObject();
   request.setEntityId(1234);
   request.setLob('testLOB');

   ResponseObject result = //the response that you want

   when(dao.updateMember(eq(request))).thenReturn(result);

   ResponseObject response = service.updateMember(request);

   // ASSERTIONS HERE

【讨论】:

由于 TO 对 Mockito 来说是新的,您可能需要添加一个示例,如何将 DAO 注入 service。另外请尝试缩进您的示例代码以提高可读性。【参考方案3】:

我所做的基本上是:当我通过服务调用调用 DAO 时,例如 '''when(AccountManagerImpl.saveAccount(testAccount)).thenReturn(mockedAccount)'''。 挑战在于该方法何时返回 void,即只是将一些数据转储到表中。这意味着您必须间接确认方法调用。就我而言,我使用'verify(mockedAccount, times(1)).saveAccount)'。 这只保证该方法至少被调用一次。当然还有更深的层次。如果您想确认数据已写入实际表,那​​么您自然需要更强大的测试工具,即设置测试数据库等。或者您可以做更便宜[并且可以说限制更少]的替代方案,而不是简单地模拟例如结果集 when(mockPreparedStmnt.execute()).thenReturn(Boolean.TRUE) 这当然会产生大量的假设,这些假设可能会反过来咬你。您基本上是在说-让我们假设数据已正确保存。但是如果这个假设本身就是 SUT 呢?我只是在代码审查期间提出它 - 这是测试覆盖率矩阵可以为您节省许多夜晚的地方。

【讨论】:

以上是关于如何在服务层单元测试中模拟数据库结果?的主要内容,如果未能解决你的问题,请参考以下文章

如何在用于运行 Java 单元测试的内存数据库中模拟 Oracle (+) 外连接表示法?

模拟 Websocket 服务器进行单元测试

如何模拟闭包以测试 Grails 服务结果?

模拟 API 响应时如何在单元测试中使用 JSON 文件

如何在单元测试中模拟 AngularFire 2 服务?

如何在 Angular 组件中模拟服务功能以进行单元测试