为什么在Objective-C中使用DI进行模拟比直接模拟对象更好?

7
这篇博客文章指出:
虽然有时可以通过模拟类方法(如OCMock示例中所示)来合理地模拟对象而不使用DI,但通常情况下这是不可能的。即使可能,测试设置的复杂性也可能超过了好处。如果您始终使用依赖注入,您将发现使用存根和模拟编写测试要容易得多。
但它没有解释为什么。在Objective-C中,DI(注入符合协议的id对象)何时比简单的OCMockito更适合用于模拟?
[given([mockArray objectAtIndex:0]) willReturn:@"first"];
 [verifyCount(mockArray, times(1)) objectAtIndex:];

?

4个回答

1
我注意到,当原始类执行一些异步操作时,为测试目标创建单独的类会更容易。
假设您编写了一个用于UIViewController的测试,该UIViewController具有LoginSystem依赖项,该依赖项使用AFNetworking向API发送请求。 LoginSystem采用块参数作为回调。(UIViewController->LoginSystem->AFNetworking)。
如果您制作LoginSystem的模拟版本,可能会遇到问题,如何触发回调块以测试UIViewController在成功/失败时的行为。当我尝试时,我用MKTArgumentCaptor检索块参数,然后必须在测试文件中调用它。
另一方面,如果您为LoginSystem创建单独的类(称其为LoginSystemStub,该类扩展自LoginSystem),则可以在3行代码和测试文件之外“模拟”行为。我们还应该保持测试文件的清洁和可读性。
另一个情况是verify()不能检查异步行为。调用expect(smth2).will.equal(smth)要容易得多。

编辑:

NSError(NSError**)的指针在使用verify()时也无法正常工作,最好创建一个存根:D


谢谢 :) 我认为它适合我的陈述:“只有在需要一些超级定制行为时才更好。” - michal.ciurus

0
答案是:它并不更好。只有在需要一些超级自定义行为时才更好。
最好的事情是,您不必为每个注入的类创建接口/协议,并且可以限制要注入的模块,使您的代码更清晰、更符合YAGNI原则。
这适用于任何动态语言或具有反射功能的语言。仅为了进行单元测试而创建如此多的混乱对我来说似乎是一个糟糕的想法。

0
想象一下,你正在尝试测试一个对象与其子对象之一交互的更复杂行为。为了确保父对象正常运行,你必须模拟子对象的所有方法,甚至可能追踪其变化状态。
但是如果这样做,你只是以混乱和复杂的方式编写了一个全新的对象。更简单的方法是编写一个全新的对象,并告诉父对象使用它。

你是在暗示使用 DI 来处理每个依赖项,带来的混乱和杂乱无章,然后创建一个单独的模拟类,比起 OCMockito 的 stubbing 更加清晰/简洁吗?我不这么认为。 - michal.ciurus
在大多数情况下,跟踪模拟状态的变化似乎是毫无意义的。你只需要从模拟中获取一些数据,并找出是否调用了某些方法。 - michal.ciurus

0

使用依赖注入(DI)可以在运行时注入模型,它不会绑定在你的类中,而只是在配置中。
当你想要模拟测试时,只需创建一个模拟模型并注入代替真实数据。除了模型之外,你只需要修改一行代码即可改变实现。

点击此处查看实际示例,或点击此处了解其背后的思想。

免责声明:当然,你也可以模拟其他东西,但这可能是最常见的用例。


是的,我知道如何使用注入模拟类进行模拟。我只是想知道是否像博客中所说的那样,使用真实类更优越,如果是的话,为什么? - michal.ciurus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接