【问题标题】:Set value to mocked object but get null将值设置为模拟对象但获取 null
【发布时间】:2016-03-16 06:40:43
【问题描述】:

我有一个简单的类Foo 被嘲笑:

public class Foo {
   private String name;
   public Foo() {
   }
   public Foo(String name) {
     this.name = name;
   }

   public void setName(String name) {
     this.name = name;
   }
   public String getName() {
     return name;
   }
}

在我的单元测试代码中,我使用Mockito 模拟它。

Foo mockedFoo = Mockito.mock(Foo.class);
mockedFoo.setName("test");
// name is null
String name = mockedFoo.getName();

我在模拟对象中设置了name,但是当我调用 getter 获取名称时,它返回 null

这是 Mockito 特有的问题,还是模拟对象无法设置值的约定?这是为什么?模拟对象下面发生了什么?

【问题讨论】:

  • when(mockedFoo.getName()).thenReturn("test");.
  • @Tom ,我知道这一点,但我想知道为什么我不能直接在模拟对象上设置值,我不是在问如何存根函数返回。
  • 因为它是一个模拟对象,它的目的不是存储东西。如果您想这样做,请创建您自己的实例。
  • @Tom,所以现在你回答了我的预期,所以,模拟对象失去了类中设计的功能是一个“约定”。
  • 约定?我称之为定义。

标签: java unit-testing mocking mockito


【解决方案1】:

是的 - Foo实际 代码无关紧要,因为你在嘲笑它……而 Mockito 不知道 setName 之间存在关系和getName。它不假定它应该将参数存储到 setName 并在调用 getName 时返回它......它可以这样做,但就我而言它不是知道的。 Mockito 提供的 mock 只允许您指定在其上调用方法时会发生什么,并检查以后调用了什么 。您可以模拟对getName() 的调用,而不是调用setName,并指定它应该返回什么...

... 或者你可以直接使用Foo 而不是模拟它。不要认为您必须在测试中模拟所有内容。当您使用真实课程时,只需模拟(或伪造)尴尬的事情,例如因为它使用文件系统或网络。

【讨论】:

  • 确实;根据我的经验,人们经常在不应该嘲笑“价值对象”类的时候。我过去考虑的一件事是,当测试尝试模拟这些类时,模拟库检测这些类,然后抛出“无效模拟”异常是否是一个好主意。可能是一个很好的“选择加入”功能添加(并且不太难实现)。
  • 但是,我们不能模拟最终方法。这种情况下怎么办
  • @Sree:这不是这个问题的目的——如果你找不到一个 确实 符合你所问的问题,我建议你提出一个新问题。 (不过我强烈怀疑会有这样的问题。)
【解决方案2】:

默认情况下,mockito 在被模拟对象中的任何方法上都没有行为。

你应该这样做:

Foo mockedFoo = Mockito.mock(Foo.class);
when(mockedFoo.getName()).thenReturn("someName");
String name = mockedFoo.getName();

编辑:正如 Jon Skeet 所提到的,您不需要模拟可以以正常方式测试的东西。

【讨论】:

  • 我们应该怎么做才能在我们的测试中调用when()?
  • import static org.mockito.Mockito.*; 就足够了。那么您可能还想从第一行删除Mockito.
【解决方案3】:

我认为,最好通过引用源代码来理解 Mockito 对您的参数类做了什么以及它返回的对象类型,Mockito Source

一般的理解是,模拟对象是一个假对象(与提供给mock() 方法的参数类型相同),没有真正的方法实现。所以基本上,在现实生活中,对模拟对象的方法调用不会发生任何事情 - 并且这就是创建模拟对象的目的(我们不希望调用真正的方法),不是吗?

我们模拟 DAO 层、LDAP 层或其他服务依赖项,因为我们不想调用对实际数据库或服务的调用。 如果我们想要真正的调用发生,我们不会创建模拟对象。

在调用 mockedFoo.setName("test");mockedFoo.getName(); 后理解发生了一些真实的事情是不正确的 - 在你调用一个模拟对象的方法之后什么都没有发生,这就是它正在实现的模拟对象的目的。

【讨论】:

    【解决方案4】:

    要解决您在代码中是否使用指定值调用 setName() 方法的问题,请使用 Mockito 验证方法。

    例如:

    verify(mockedFoo, times(1)).setName("test");
    

    将验证带有字符串参数“test”的 mockedFoo.setName() 方法在被测代码中被调用了一次。

    【讨论】:

      【解决方案5】:

      您需要在模拟对象上调用真实方法。使用@Spy

      【讨论】:

        【解决方案6】:

        有点用这种方法。我为 EasyMock 编写过,但使用 Mockito 也可以使用它。

        String nameMock = createNiceMock(String.class);
        Foo MockedFoo = new Foo();
        Foo.setName(nameMock);
        
        EasyMock.expect(Foo.getName()).andReturn(nameMock);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-07-15
          • 2022-10-17
          • 1970-01-01
          • 2010-10-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多