设计 class 调用外部 API 时测试实现是否正确?
Is testing implementation correct when designing class calling external API?
我从一些遵循这些原则的原型代码开始:
// Omitted most definitions, return values checks, etc.
// The real code is much bigger and uglier.
serverId = socket(AD_INET, SOCK_STREAM, PROTO_ANY);
setsockopt(serverId, SOL_SOCKET, SO_REUSEADDR, &reuseAddrOk, sizeof(int));
bind(serverId, &serverAddress, sizeof(serverAddress));
listen(serverId, waitQueueSize);
clientId = accept(serverId, &clientAddress, sizeof(clientAddress));
read(clientId, clientBuffer, charsToRead);
现在我想用非常简单的 classes 重构这个代码提取功能(暂时不想让事情变得非常通用......YAGNI)。这是我正在考虑的界面类型:
SocketServer server = SocketServer(parameters);
// SocketServer knows how to create a SocketClient...abstract factories, dependency injection, etc. etc.
SocketClient client = server.accept();
string clientMessage = client.read();
client.write(serverMessage);
例如,SocketServer
class 封装了用于创建新套接字服务器的所有样板文件:
SocketServer server = SocketServer(parameters);
然后,因为这需要调用系统API,我需要模拟它:
SocketServer server = SocketServer(systemAPI, parameters);
现在,测试此代码是否正确是什么意思?它不会产生任何我可以检查的输出(或者更好的是,我所做的所有这些都是为了封装输出,比如文件描述符)。我可以检查是否调用了 mock API 的正确方法,例如:
testSocketCalledWithCorrectParameters() {
systemAPI = mock(SystemAPI).expect(once()).method("socket").with(
SystemAPI.AF_INET,
SystemAPI.SOCK_STREAM,
SystemAPI.PROTO_AUTO
);
ServerSocket(systemAPI, parameters);
}
在这种情况下我应该依赖于测试实现而不是接口是否合适?是否被迫测试一个实现而不是一个糟糕设计的界面?
我能想到的所有其他测试都设定了对实施的期望:
testServerSocketIsCreatedWithCorrectDescriptor() {
dummyDescriptor = 10;
systemAPI = mock(SystemAPI).when("socket").return(dummyDescriptor);
server = SocketServer(systemAPI, parameters);
assertEquals(dummyDescriptor, server.descriptor);
}
/**
* @expected SocketException
*/
testThrowsExceptionIfErrorCreatingSocket() {
systemAPI = mock(SystemAPI).when("socket").return(SystemAPI.RETURN_ERROR);
SocketServer(systemAPI, parameters);
}
// etc.
然后,我是否也应该为 socketAPI
编写单元测试,或者我是否应该理所当然地认为它将是一个非常愚蠢的包装器 class,除了将调用委托给外部 API(因此不需要测试)?
我试着一一解答
1.测试调用属性
这种测试只有文档价值(这是调用 API 的方式)检查,如果它真的值得 -> 也许你的代码中的语句足够清楚。
2。 testServerSocketIsCreatedWithCorrectDescriptor:
对我来说,这种测试更有价值。我几乎总是写一个 testCreation 来显示:输入参数和断言属性,例如一辆汽车需要轮子来建造,默认情况下有 4 个轮子,红色和一个方向盘。
3。 testThrowsExceptionIfErrorCreatingSocket
这种测试最有价值。它们定义和保护您的 class 在不同情况下的行为。我在这里遗漏了一个断言,例如抛出的异常类型。
尽可能多地写这种测试。
4.测试系统API
没有。永远不要在单元测试中测试系统行为。特别是没有模拟。
这是模块或端到端测试的一部分。
希望对您有所帮助。
我从一些遵循这些原则的原型代码开始:
// Omitted most definitions, return values checks, etc.
// The real code is much bigger and uglier.
serverId = socket(AD_INET, SOCK_STREAM, PROTO_ANY);
setsockopt(serverId, SOL_SOCKET, SO_REUSEADDR, &reuseAddrOk, sizeof(int));
bind(serverId, &serverAddress, sizeof(serverAddress));
listen(serverId, waitQueueSize);
clientId = accept(serverId, &clientAddress, sizeof(clientAddress));
read(clientId, clientBuffer, charsToRead);
现在我想用非常简单的 classes 重构这个代码提取功能(暂时不想让事情变得非常通用......YAGNI)。这是我正在考虑的界面类型:
SocketServer server = SocketServer(parameters);
// SocketServer knows how to create a SocketClient...abstract factories, dependency injection, etc. etc.
SocketClient client = server.accept();
string clientMessage = client.read();
client.write(serverMessage);
例如,SocketServer
class 封装了用于创建新套接字服务器的所有样板文件:
SocketServer server = SocketServer(parameters);
然后,因为这需要调用系统API,我需要模拟它:
SocketServer server = SocketServer(systemAPI, parameters);
现在,测试此代码是否正确是什么意思?它不会产生任何我可以检查的输出(或者更好的是,我所做的所有这些都是为了封装输出,比如文件描述符)。我可以检查是否调用了 mock API 的正确方法,例如:
testSocketCalledWithCorrectParameters() {
systemAPI = mock(SystemAPI).expect(once()).method("socket").with(
SystemAPI.AF_INET,
SystemAPI.SOCK_STREAM,
SystemAPI.PROTO_AUTO
);
ServerSocket(systemAPI, parameters);
}
在这种情况下我应该依赖于测试实现而不是接口是否合适?是否被迫测试一个实现而不是一个糟糕设计的界面?
我能想到的所有其他测试都设定了对实施的期望:
testServerSocketIsCreatedWithCorrectDescriptor() {
dummyDescriptor = 10;
systemAPI = mock(SystemAPI).when("socket").return(dummyDescriptor);
server = SocketServer(systemAPI, parameters);
assertEquals(dummyDescriptor, server.descriptor);
}
/**
* @expected SocketException
*/
testThrowsExceptionIfErrorCreatingSocket() {
systemAPI = mock(SystemAPI).when("socket").return(SystemAPI.RETURN_ERROR);
SocketServer(systemAPI, parameters);
}
// etc.
然后,我是否也应该为 socketAPI
编写单元测试,或者我是否应该理所当然地认为它将是一个非常愚蠢的包装器 class,除了将调用委托给外部 API(因此不需要测试)?
我试着一一解答
1.测试调用属性
这种测试只有文档价值(这是调用 API 的方式)检查,如果它真的值得 -> 也许你的代码中的语句足够清楚。
2。 testServerSocketIsCreatedWithCorrectDescriptor:
对我来说,这种测试更有价值。我几乎总是写一个 testCreation 来显示:输入参数和断言属性,例如一辆汽车需要轮子来建造,默认情况下有 4 个轮子,红色和一个方向盘。
3。 testThrowsExceptionIfErrorCreatingSocket
这种测试最有价值。它们定义和保护您的 class 在不同情况下的行为。我在这里遗漏了一个断言,例如抛出的异常类型。 尽可能多地写这种测试。
4.测试系统API
没有。永远不要在单元测试中测试系统行为。特别是没有模拟。 这是模块或端到端测试的一部分。
希望对您有所帮助。