这取决于您要测试的内容。但让我们假设您的测试范围仅是您自己的代码。在这种情况下,您应该测试:
- “高兴的情况”:为您的应用程序提供有效的输入,并确保它产生正确的输出。
- 失败案例:向您的应用程序提供无效输入,并确保它正确处理了它们。
为此,您不能使用同事的组件:而是使用嘲讽,即用可以从测试框架控制的“假”模块替换应用程序的其余部分。具体执行方式取决于模块接口的方式。只需用硬编码的参数调用模块的方法就足够了,并且它可能变得复杂,就像编写将其他模块的公共接口与测试环境连接在一起的整个框架一样。
不过,那只是单元测试用例。您还需要集成测试,在该测试中您可以同时测试所有模块。再次,您想测试成功的情况和失败的情况。
在“基本示例”中,要对代码进行单元测试,请编写一个模拟数据库层的模拟类。但是,您的模拟类并没有真正进入数据库:您只是将其与预期的输入和固定的输出一起预加载。用伪代码:
function test_ValidUser() {
// set up mocking and fixtures
userid = 23;
db = new MockDB();
db.fixedResult = { firstName: "John", lastName: "Doe" };
db.expectedCall = { method: 'getUser', params: { userid: userid } };
userController = new UserController(db);
expectedResult = "John Doe";
// run the actual test
actualResult = userController.displayUserAsString(userid);
// check assertions
assertEquals(expectedResult, actualResult);
db.assertExpectedCall();
}
这是您如何测试正确报告的缺失字段的方法:
function test_IncompleteUser() {
// set up mocking and fixtures
userid = 57;
db = new MockDB();
db.fixedResult = { firstName: "John", lastName: "NA" };
db.expectedCall = { method: 'getUser', params: { userid: userid } };
userController = new UserController(db);
// let's say the user controller is specified to leave "NA" fields
// blank
expectedResult = "John";
// run the actual test
actualResult = userController.displayUserAsString(userid);
// check assertions
assertEquals(expectedResult, actualResult);
db.assertExpectedCall();
}
现在事情变得有趣了。如果真正的数据库类行为不当怎么办?例如,出于不清楚的原因,它可能会引发异常。我们不知道它是否可以,但是我们希望我们自己的代码能够优雅地处理它。没问题,我们只需要使我们的MockDB抛出异常即可,例如,通过添加如下方法:
class MockDB {
// ... snip
function getUser(userid) {
if (this.fixedException) {
throw this.fixedException;
}
else {
return this.fixedResult;
}
}
}
然后我们的测试用例如下所示:
function test_MisbehavingUser() {
// set up mocking and fixtures
userid = 57;
db = new MockDB();
db.fixedException = new SQLException("You have an error in your SQL syntax");
db.expectedCall = { method: 'getUser', params: { userid: userid } };
userController = new UserController(db);
// run the actual test
try {
userController.displayUserAsString(userid);
}
catch (DatabaseException ex) {
// This is good: our userController has caught the raw exception
// from the database layer and wrapped it in a DatabaseException.
return TEST_PASSED;
}
catch (Exception ex) {
// This is not good: we have an exception, but it's the wrong kind.
testLog.log("Found the wrong exception: " + ex);
return TEST_FAILED;
}
// This is bad, too: either our mocking class didn't throw even when it
// should have, or our userController swallowed the exception and
// discarded it
testLog.log("Expected an exception to be thrown, but nothing happened.");
return TEST_FAILED;
}
这些是您的单元测试。对于集成测试,您不使用MockDB类。而是将两个实际的类链接在一起。您仍然需要固定装置。例如,您应该在运行测试之前将测试数据库初始化为已知状态。
现在,就职责范围而言:您的代码应期望代码库的其余部分按照规范进行实现,但是还应准备好在其余部分拧紧时妥善处理。你是不是负责测试其他代码不是你自己的,但你是负责使你的代码弹性的另一端行为不端的代码,你也有责任测试代码的应变能力。这就是上面的第三项测试。