我假设您使用的是 Objective-C。对于 Objective-C , OCMock广泛用于模拟/单元测试(您的第二个选项)。
一年多前我最后一次使用 OCMock,但据我所知,它是一个成熟的模拟框架,可以完成下面描述的所有事情。
关于 mock 的一件重要事情是,您可以尽可能多地使用对象的实际功能。您可以创建一个“空”模拟(其中所有方法都是您的对象,但不会执行任何操作)并仅覆盖您在测试中需要的方法。这通常在测试依赖于模拟的其他对象时完成。
或者您可以创建一个模拟您的真实对象的行为,并删除一些您不想在该级别测试的方法(例如 - 实际访问数据库的方法,需要网络连接等)。这通常在您测试模拟对象本身时完成。
重要的是要了解您不会一劳永逸地创建模拟。每个测试都可以根据正在测试的内容重新为相同的对象创建模拟。
关于模拟的另一个重要的事情是,您可以“记录”场景(调用序列)和您对它们的“期望”(应该调用幕后的哪些方法,使用哪些参数以及以何种顺序),然后“重播”场景 - 如果没有达到预期,测试将失败。这是经典 TDD 和 mockist TDD 之间的主要区别。它有其优点和缺点(参见 Martin Fowler 的文章)。
现在让我们考虑您的具体示例(我将使用看起来更像 C++ 或 Java 而不是 Objective C 的伪语法):
假设您有一个LoginForm
表示输入的登录信息的类对象。它有(除其他外)方法setName(String)
、、、setPassword(String)
和。bool authenticateUser()
Authenticator* getAuthenticator()
您还有一个类对象,Authenticator
它具有(除其他外)方法bool isRegistered(String user)
、、bool authenticate(String user, String password)
和bool isAuthenticated(String user)
。
以下是测试一些简单场景的方法:
除了上面提到的四个之外,创建MockLoginForm
所有方法都为空的模拟。前三种方法将使用实际LoginForm
实现;getAuthenticator()
将被打掉返回MockAuthenticator
。
创建MockAuthenticator
将使用一些假数据库(例如内部数据结构或文件)来实现其三个方法的模拟。数据库将只包含一个元组:('rightuser','rightpassword')
.
测试用户未注册
回放场景:
MockLoginForm.setName('wronuser');
MockLoginForm.setPassword('foo');
MockLoginForm.authenticate();
期望:
getAuthenticator() is called
MockAuthenticator.isRegistered('wrognuser') is called and returns 'false'
测试错误密码
回放场景:
MockLoginForm.setName('rightuser');
MockLoginForm.setPassword('foo');
MockLoginForm.authenticate();
期望:
getAuthenticator() is called
MockAuthenticator.isRegistered('rightuser') is called and returns 'true'
MockAuthenticator.authenticate('rightuser','foo') is called and returns 'false'
测试登录确定
回放场景:
MockLoginForm.setName('rightuser');
MockLoginForm.setPassword('rightpassword');
MockLoginForm.authenticate();
result = MockAuthenticator.isAuthenticated('rightuser')
期望:
getAuthenticator() is called
MockAuthenticator.isRegistered('rightuser') is called and returns 'true'
MockAuthenticator.authenticate('rightuser','rightpassword') is called and returns 'true'
result is 'true'
我希望这有帮助。