拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 Mockito MockSettings概述

Mockito MockSettings概述

白鹭 - 2021-11-24 584 0 0

1.概述

通常,Mockito为我们的模拟对象提供的默认设置应该绰绰有余。

但是,有时可能需要在模拟创建期间提供其他模拟设置。这在调试,处理遗留代码或涵盖一些极端情况时可能很有用。

在先前的教程中,我们学习了如何使用宽大的模拟。在本快速教程中,我们将学习如何使用MockSettings界面提供的其他一些有用功能。

2.模拟设置

简而言之, MockSettings接口提供了Fluent API,使我们能够在模拟创建过程中轻松添加和组合其他模拟设置。

创建模拟对象时,所有模拟都带有一组默认设置。让我们看一个简单的模拟示例:

List mockedList = mock(List.class);

在幕后,Mockito mock方法委托给另一个重载方法,该方法具有一组模拟默认设置:

public static <T> T mock(Class<T> classToMock) {

 return mock(classToMock, withSettings());

 }

让我们看一下我们的默认设置:

public static MockSettings withSettings() {

 return new MockSettingsImpl().defaultAnswer(RETURNS_DEFAULTS);

 }

如我们所见,模拟对象的标准设置非常简单。我们为模拟互动配置默认答案。通常,使用RETURNS_DEFAULTS将返回一些空值。

摆脱这一点的重要一点是,如果需要,我们可以为模拟对象提供一组自定义设置

在接下来的部分中,我们将看到一些方便使用的示例。

3.**提供不同的默认答案**

现在,我们对模拟设置有了更多的了解,让我们看看如何更改模拟对象的默认返回值。

假设我们有一个非常简单的模拟设置:

PizzaService service = mock(PizzaService.class);

 Pizza pizza = service.orderHouseSpecial();

 PizzaSize size = pizza.getSize();

当我们按预期运行此代码时,将得到NullPointerException因为未打桩的方法orderHouseSpecial返回null

可以,但是有时在使用旧版代码时,我们可能需要处理复杂的模拟对象层次结构,并且定位这些类型的异常发生的位置可能很耗时。

为了帮助我们解决这个问题,我们可以在模拟创建过程中通过模拟设置提供其他默认答案

PizzaService pizzaService = mock(PizzaService.class, withSettings().defaultAnswer(RETURNS_SMART_NULLS));

通过使用RETURNS_SMART_NULLS作为我们的默认答案,Mockito向我们提供了一条更有意义的错误消息,该错误消息向我们准确显示了发生错误的存根的位置:

org.mockito.exceptions.verification.SmartNullPointerException:

 You have a NullPointerException here:

 -> at com.baeldung.mockito.mocksettings.MockSettingsUnitTest.whenServiceMockedWithSmartNulls_thenExceptionHasExtraInfo(MockSettingsUnitTest.java:45)

 because this method call was *not* stubbed correctly:

 -> at com.baeldung.mockito.mocksettings.MockSettingsUnitTest.whenServiceMockedWithSmartNulls_thenExceptionHasExtraInfo(MockSettingsUnitTest.java:44)

 pizzaService.orderHouseSpecial();

确实可以在调试测试代码时为我们节省一些时间。 Answers枚举还提供其他一些预配置的注意模拟答案:

  • RETURNS_DEEP_STUBS –返回深层存根的答案–在使用Fluent API时可能很有用
  • RETURNS_MOCKS –使用此答案将返回普通值,例如空集合或空字符串,然后,它尝试返回模拟
  • CALLS_REAL_METHODS –顾名思义,当我们使用此实现时,未打桩的方法将委托给实际的实现

4.命名模拟和详细记录

我们可以通过使用MockSettingsname方法为模拟对象命名。这对于调试特别有用,因为我们提供的名称用于所有验证错误:

PizzaService service = mock(PizzaService.class, withSettings()

 .name("pizzaServiceMock")

 .verboseLogging()

 .defaultAnswer(RETURNS_SMART_NULLS));

在此示例中,我们通过使用verboseLogging()方法将此命名功能与详细日志记录结合在一起。

使用此方法可以实时记录到标准输出流中,以便对此模拟方法进行方法调用。同样,可以在测试调试期间使用它来查找与模拟的错误交互。

运行测试时,我们将在控制台上看到一些输出:

pizzaServiceMock.orderHouseSpecial();

 invoked: -> at com.baeldung.mockito.mocksettings.MockSettingsUnitTest.whenServiceMockedWithNameAndVerboseLogging_thenLogsMethodInvocations(MockSettingsUnitTest.java:36)

 has returned: "Mock for Pizza, hashCode: 366803687" (com.baeldung.mockito.fluentapi.Pizza$MockitoMock$168951489)

有趣的是,如果我们使用@Mock注释,我们的模拟将自动将字段名称作为模拟名称。

5.模拟额外的接口

有时,我们可能想指定模拟应实现的额外接口。同样,当使用无法重构的旧代码时,这可能很有用。

假设我们有一个特殊的接口:

public interface SpecialInterface {

 // Public methods

 }

和使用该接口的类:

public class SimpleService {



 public SimpleService(SpecialInterface special) {

 Runnable runnable = (Runnable) special;

 runnable.run();

 }

 // More service methods

 }

当然,这不是干净的代码,但是如果我们被迫为此编写单元测试,则很可能会遇到问题:

SpecialInterface specialMock = mock(SpecialInterface.class);

 SimpleService service = new SimpleService(specialMock);

运行此代码时,将得到ClassCastException为了解决这个问题,我们可以使用extraInterfaces方法创建具有多种类型的模拟

SpecialInterface specialMock = mock(SpecialInterface.class, withSettings()

 .extraInterfaces(Runnable.class));

现在,我们的模拟创建代码不会失败,但是我们应该真正强调,强制转换为未声明的类型并不是很酷。

6.提供构造函数参数

在最后一个示例中,我们将看到如何使用MockSettings调用带有参数值的实际构造函数:

@Test

 public void whenMockSetupWithConstructor_thenConstructorIsInvoked() {

 AbstractCoffee coffeeSpy = mock(AbstractCoffee.class, withSettings()

 .useConstructor("espresso")

 .defaultAnswer(CALLS_REAL_METHODS));



 assertEquals("Coffee name: ", "espresso", coffeeSpy.getName());

 }

这次,Mockito在创建AbstractCoffee模拟的实例时尝试使用带有String值的构造函数。我们还配置了默认答案,以委托给实际的实现。

如果我们在构造函数中有一些逻辑要测试或触发,以使我们的类处于某种特定状态,这可能很有用。监视抽像类时,它也很有用。

7.结论

在本快速教程中,我们了解了如何使用其他模拟设置来创建模拟。

但是,我们应该重申,尽管这有时是有用的,并且可能是不可避免的,但在大多数情况下,我们应该努力使用简单的模拟编写简单的测试。

与往常一样,可以在GitHub上获得本文的完整源代码。

标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *