objective-c - 如何对 Internet 协议(protocol)实现进行单元测试?

标签 objective-c cocoa unit-testing sockets ocmock

我决定将单元测试添加到我的项目中,并以测试驱动的方式继续开发。我目前正在为我的 ManageSieve 客户端对象实现单元测试,但我不确定测试该野兽的最佳方法是什么。

我的 SieveClient 对象依赖于另外两个对象进行网络通信:CocoaAsyncSocket 和我自己的 SaslConn 对象,它是我对处理身份验证方法的 Cyrus SASL 库。为了进行测试,我需要用模拟对象替换它们。为此,我将使用 OCMock 框架。我不太确定如何执行此操作,因为 SieveClient 对象需要自己创建这些对象。现在我覆盖该对象的(私有(private))setter 以始终使用 OCMocks partialMockForObject: 方法安装我的模拟对象。但这对我来说感觉不对。有什么想法可以更好地解决这个问题吗?

我遇到麻烦的另一部分是套接字本身。为了能够测试协议(protocol)细节,我需要一种从套接字返回预定义测试数据的方法。我想我可以只使用 OCMock 机制来伪造套接字的返回值。但是由于 CocoaAsyncSocket 提供了许多不同的方法来从套接字中读取数据,我必须确切地知道协议(protocol)对象以何种顺序使用了哪些方法。我不希望我的单元测试依赖于我的协议(protocol)对象的实现细节。那么我应该在这里做什么?手动为套接字类实现模拟对象?这看起来很重要,所以我可能也需要单元测试。这是个好主意吗?

我读到过,如果某些东西很难测试,那么它可能也没有设计得很好。但我看不出如何做得更好,因为困难的部分在于与我必须做的套接字交互。

如果您想查看代码,可以在 Bitbucket 上找到它:SieveClient.mSieveClient.h

编辑:依赖注入(inject)

所以我阅读了有关依赖注入(inject)的内容,我想我将使用它来将 AsyncSocketSaslConn 对象放入我的 SieveClient 对象。我将更改我的构造函数以接受这些对象并使用它们。由于此类的用户通常不关心套接字和 SASL 对象,我将添加一个工厂方法(以方便的构造函数的形式),它只创建这些对象并将它们传递给构造函数。

但这只解决了我的测试问题的第一部分(也是更简单的部分)。

最佳答案

But then I rejected the idea, because it wouldn’t help very much. I could test the SieveClient object more easily, true. But then I’d have the same problems with testing the new object. Seems to me that this is just putting up the trouble for later. Especially since I have nothing I could re-use the new class for.

这不会是同一个问题。

我假设您需要 SieveClient 来控制内部其他对象的实例化,因为它是您不想公开的 API 的一部分。如果是这个原因,通过将它们分开,您将不再有相同的需求,因为您可以让SieveClient 控制绑定(bind),而另一部分执行协议(protocol)接收实例与一起工作。

通过执行上述操作,您可以将模拟对象交给您的协议(protocol)实现然后,这些模拟将具有您可能需要的任何期望。如果您发现它最终会过于复杂,那么您可能需要重新调整职责,这通常会导致更清晰/更简单的协议(protocol)实现(如果您发现您需要进行那些单元测试)。

如上所述,您还需要考虑您尝试测试的代码是否尽可能专注于协议(protocol)并且没有任何额外的元素。如果是这样的话,那么对它进行单元测试就不是一个好的候选者,因为它的唯一职责是与外部系统的交互。我会决定协议(protocol)规范在这个系统中有多重要,如果它都是关于与外部系统的集成,我会把它当作一个集中的集成测试,而不是触及真正的外部系统,并与单元测试分开(因此它不会影响运行系统其余部分的单元测试所需的速度)。


由于编辑而重新阅读问题后,我必须强调我在上面所说的关于重点集成测试的内容。你问:

But since CocoaAsyncSocket provides many different methods to read data from the socket I have to know exactly which are being used by the protocol object in which order. I don’t want my unit test to be that dependent on implementation details of my protocol object. So what should I do here? Implement a mock object for the socket class by hand? This seems non-trivial, so I’d probably need unit tests for that too. Is that a good idea?

如果您正在处理一个非常复杂的对象,并且该对象完全是关于超越边界的集成,那么您通常最好避免将其作为单元测试的一部分。在那种情况下,您需要一个集中的集成测试/来命中真正的外部系统。 这并不意味着其余代码的所有单元测试都会影响外部系统,只是使用该对象的非常简单的代码/类单元

在您的场景中,此类对象很可能是 SieveClient,在这种情况下,请忽略那段代码的单元测试。您要做的是在测试使用它的代码时模拟 SieveClient。另一方面,如果您发现 SieveClient 远不止于此,您想要添加一个类来简化这些通信方面,这将是您在测试 SieveClient 时模拟的内容,也是您进行集中集成测试的对象。

这种类型的测试是确保与外部交互的代码按预期工作的一种非常有效的方法,因为这是类和相关测试的重点。 万一外部系统上的某些东西开始以不同方式工作,您会清楚地注意到它 - 而不是将它与您的应用程序逻辑混合或最糟糕的是根本不进行测试。

关于objective-c - 如何对 Internet 协议(protocol)实现进行单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3641982/

相关文章:

ios - 无效的签名请求 : Missing required parameter

objective-c - 使用 [[NSRunLoop currentRunLoop] runUntilDate :[NSDate date]] to let the scheduled selectors fire

objective-c - cocoa 中的单例,他们是邪恶的吗?

typescript - Jasmine-ts 抛出关于包子路径的错误

java - spring 注入(inject)模拟未使用

objective-c - iOS - 识别之前在 UINavigationController 中显示的 UIViewController

ios - 如何从 Xcode UI 测试访问核心数据存储?

objective-c - Cocoa 从子类实例移动到父类

objective-c - subview 中的 NSView TouchesMovedWithEvent 正在暂停带有内置触控板的 SKScene,但不是魔术触控板?

unit-testing - 为以下 meteor 代码写一个mocha单元测试代码