首先,让我们直接了解几个定义:
单元测试是您编写的一小段代码,用于确保您开发的系统行为正确。单元测试通常是在 这样的框架中创建的,但不需要。例如,单元测试将 .JUnit
assertEquals(5, calculator.add(2, 3))
模拟是对真实系统的模仿,通常用于单元测试。模拟通常是在 or 这样的框架中创建的,但不需要。Mocks是“测试双精度”的一种形式 - 用于测试目的而替换实际系统的代码的总称 - Martin Fowler在一篇名为“Mocks Are't Stubs”的文章中更准确地定义了它们。Mockito
EasyMock
编写单元测试时,您尝试测试单个单元,这通常称为被测系统或简称为 SUT。每个测试都可能有不同的系统在测试中,关键是你在测试中测试的代码是你真实的、实际的代码,而不是任何类型的模拟或假实现。
在复杂的应用程序中,您的类可能需要与其他类协作,这些类可能被编写或测试,也可能不被编写或测试,这些类被明确称为依赖项。任何给定的类或系统可能有自己的依赖项,并且可能是其他系统的依赖项。模拟框架适用于模拟这些依赖项,而不是模拟所测试的系统。
对于您的示例,QAService 是您正在编写的主要类(受测系统),QAServiceTest 是该系统的单元测试。不需要任何模拟。
假设 QAService 依赖于另一个尚未编写的类,称为“StorageService”。在编写 QAService 的测试之前,您不一定希望等待 StorageService 工作,因此,您不是使用真正的 StorageService,而是使用模拟。同样,在名为 QAServiceTest 的单元测试中,使用真正的 QAService 并模拟其依赖项 StorageService。
即使您没有编写 StorageService,您可能也对 QAService 如何使用该类有一些期望。也许您知道,当您调用 时,它应该返回一个数字ID,如 .即使没有代码工作,您也可以创建一个 Mockito ,并像这样准备它:storageService.store(qaRecord)
101
mockStorageService
when(mockStorageService.store(expectedQARecord)).thenReturn(101);
现在,假设在测试结束时,您希望确保正在测试的QAService方法将绝对调用 。Mockito会像这样检查:storageService.delete(101)
mockStorageService
verify(mockStorageService).delete(101);
通常不需要验证您使用的语句,因为除非被测系统正确调用 mock 以获取该返回值(此处为 101),否则测试不太可能成功。when
现在,假设您已经编写了另一个名为 QAApplication 的代码块,您正在一个名为 QAApplicationTest 的单元测试中对其进行测试,该测试依赖于 QAService。您可能没有完成或测试 QAService,并且使用真正的 QAService 将需要存储服务,因此,您可以在单元测试中使用模拟 QAService 和真正的 QAApplication,称为 QAApplicationTest。
因此,总而言之,模拟在单元测试中工作,以模拟被测系统的依赖关系。在您的情况下,QAServiceTest 不需要模拟 QAService,但可用于模拟 QAService 的依赖项。如果您确实需要一个模拟 QAService,则在测试另一个类时(其中 QAService 本身是依赖项)时,您将需要它。