【问题标题】:When you mock a class in mockito framework, do you stand up actual mock class manually?当你在 mockito 框架中模拟一个类时,你会手动建立实际的模拟类吗?
【发布时间】:2018-12-14 16:34:04
【问题描述】:

我对 mockito 框架相当陌生。我一直在阅读有关它的多个教程。我关注的其中之一是:https://www.tutorialspoint.com/mockito/mockito_first_application.htm

有一个创建 Stock Service 模拟的语句。

In this example, we've created a mock of Stock Service to get the dummy price of some stocks

我的问题是 Stock Servicea real service classmock service class 你必须手动站起来模仿真实的服务类。我有点困惑。对junit框架有基本的了解。我之前练习的是,如果有一个服务类Foo,那么我使用提供所有公开方法的实际类。

public class Foo {

    public Foo() { } // construtor

    public String returnAddress(String userId) {
        // ...
        return dataAccesobj.getAddress(userId);
    }
}

如果我没记错的话,在单元测试中调用foo.returnAddress(..)

我问这个问题的原因是当我使用mockito为一个类创建一个测试方法时,我遇到了一个独特的(?)挑战。

我从一个真正的服务类开始,它依赖于它的超类构造函数来返回它的实例。我遇到的挑战是这个超类构造函数启动数据库连接和加载/解析我的测试不需要的属性文件。我正在考虑如何防止数据库连接和加载/读取道具文件....

我想我读过mockito 教程之一,您可以在没有此类服务的情况下隔离测试。我尝试使用@Mock@Spy(还没有完全理解它们的用途......)但它对输出没有影响(也许我滥用了这些注释)。

所以我所做的实际上是从真实服务类(例如Foo)中创建假/模拟类,只需复制它并将其重命名为FooMock,并将其放入单元测试类所在的src/test/java文件夹中从运行。除了去掉不需要的逻辑,例如db connectionloading/reading prop file for env specific,我保持模拟类与真实服务类完全相同。通过这样做,我能够测试读取 ldap 目录的公开方法之一......

很抱歉我跑题了,但希望我的观点在这一点上是明确的。我不确定我处理这种情况的方式是对还是错。我希望有经验的工程师能澄清我处理此事的方式是否可以以mockito 的方式接受。如果没有,请告诉我最好的处理方法。

【问题讨论】:

  • Foo mockFoo = Mockito.mock(Foo.class) ...当您调用时,您会看到它调用构造函数吗?每 this question mockito 不调用构造函数本身。我不清楚你的问题是什么。
  • 如果描述不清楚,我深表歉意。是的,它触发Foo 的私有构造函数,该构造函数调用其父类的super()。顺便说一句 - Foo 类使用单例模式。我使用注释 InjectMocks 代替返回 Foo 实例顺便说一句。
  • 我尝试了Foo fooMock = Mockito.mock(Foo.class),然后它抛出了一个异常Cannot mock/spy class Foo Mockito cannot mock/spy following: final classes。确实是final 类。
  • 您可能会考虑使用 PowerMockito 来解决最终类限制。

标签: java unit-testing mockito


【解决方案1】:

有了 Mockito, 模拟是包装类的实现。 模拟对象“包装”模拟的目标 (您示例中的服务) 并允许您定义每种方法的功能。

Mockito 有两个模拟功能选项; 调用被包装的方法,不要调用被包装的方法。

我不知道什么时候调用包装的方法才有意义, 所以我总是使用不要调用包装的方法。

创建模拟后, 使用 Mockito.doReturn(returnvalue).when(mockObject).method(method parameters) 方法来模拟功能。

编辑:更多信息。

我假设您使用的是 junit v4。 详细信息将根据 junit 主要版本号而有所不同, 但实际工作是一样的。

  1. 使用注解来定义你的 Mock 对象 (@Mock), 除了少数特殊情况。 这将创建非最终类的模拟, 抽象类, 和接口。
  2. 使用@Before注解创建一个“测试前”方法; 我传统上将此方法命名为preTestSetup, 但实际名称并不重要。
  3. 调用MockitoAnnotations.initMocks(this)作为第一行代码 在“测试前”方法中。 这将找到 @Mock 注释并为每个注释实例化一个模拟。
  4. 使用ReflectionTestUtils.setField 方法将模拟注入到您的对象中(假设您没有setter 方法, 我历来不喜欢)。
  5. 使用Mockito.doReturn(returnvalue).when(mockObject).method(method parameters) 技术定义每个方法的模拟功能。

这是一些示例代码 (警告: 这应该是功能齐全的, 但我没有编译它):

public interface MyService
{
    String blammy(SomeParameter parameter);
}

public class UsesMyService
{
    @Autowired
    private MyService myService;

    public String kapow(final SomeParameter parameter)
    {
        return myService.blammy(parameter);
    }
}

public class UnitTestUsesMyService
{
    private UsesMyService classToTest;

    @Mock
    private MyService mockMyService;

    @Mock
    private SomeParameter mockSomeParameter;

    @Before
    public void preTestSetup()
    {
        MockitoAnnotations.initMocks(this);

        classToTest = new UsesMyService();

        doReturn("Blam").when(mockMyService).blammy(mockSomeParameter);

        ReflectionTestUtils.setField(
            classToTest,
            "myService",
            mockMyService);
    }

    @Test
    public void kapow_allGood_success()
    {
        final String actualResult;


        actualResult = classToTest.kapow(mockSomeParameter);


        assertNotNull(actualResult); // Not strictly necessary.

        assertEquals(
            "Blam",
            actualResult);
    }
}

【讨论】:

  • 我仍在为如何正确获取模拟对象而苦苦挣扎,但Mockito.doReturn(returnvalue).when(mockObject).method(method parameters) method to mock functionality. 听起来很有希望。我将用我获得非正统方式的当前模拟对象对其进行测试。你的建议!
  • 感谢您抽出宝贵的时间来增强您的答案以帮助我。有些部分我想我明白,但有些我不明白。拥有private 课程,我不能使用mock,所以我决定改用junit,尽管建议使用powermock,但由于某种原因我不能使用它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-18
  • 1970-01-01
相关资源
最近更新 更多