在我的应用程序中,我们的设计有两层:API和操作。
1. 操作实现代码的“真实”逻辑,例如:验证用户,检索书籍信息,通知用户他的书已被查看。
许多API都可以使用相同的操作。
2. API 由用户执行:他们接收参数,然后根据API的逻辑执行各种操作。
例如:ViewBookAPI:
class BookApis
{
/**
* authenticateUserOperation, retreiveBookOperation, informUserBookViewOperation
* are injected to this class. (Dependency Injection)
*/
public function viewBookApi($bookId, $accessToken)
{
$internalUserId = $this->authenticateUserOperation($accessToken);
$book = $this->retrieveBookOperation($bookId, $internalUserId);
$this->informUserBookWasViewedOperation($book->getOwnerUserId(), $bookId);
return $book->getContent();
}
}
我该如何测试这个设计?
1.如果我测试API,那么我将不得不对使用相同操作的API重复相同的测试。
2.如果我测试操作,我所要做的就是验证API是否正确使用了这些操作。
但是如果将错误的对象注入API会怎样?那么没有测试会失败。
非常感谢。
答案 0 :(得分:2)
我在这里感到矛盾。 好像你有
从那以后,我会说你需要测试两者 *操作本身可能正确实现。只是进行API测试可能会导致单个错误的多个客户端/测试失败 * API可能不会调用必需(和实现)操作......即使操作测试可能会通过。
所以:
答案 1 :(得分:2)
你的设计非常普遍(理所当然),所以我对这个问题不断出现感到有些惊讶。
您需要两种类型的测试:
集成测试非常自我解释(如果没有,请告诉我,我会详细说明),所以我猜你是指单元测试。需要对两个不同的层进行不同的测试。
操作层:
在这里,您要检查执行实际工作的类是否正常工作。这意味着您应该实例化您正在测试的类,输入它并检查它提供的输出是否符合您的期望。
假设您有这类课程:
public class OperationA {
public int multiply(int x, int y) {
return x * y;
}
}
检查它是否符合您的预期意味着编写一个测试,例如(测试用例本身只是一个例子,不要太认真):
public class OperationATest {
@Test
public void testMultiplyZeroByAnyNumberResultsInZero() {
OperationA op = new OperationA();
assertEquals(0, op.multiply(0, 0));
assertEquals(0, op.multiply(10, 0));
assertEquals(0, op.multiply(-10, 0));
...
}
@Test
public void testMultiplyNegativeByNegativeResultsInPositive() {
...
}
...
}
API层:
在这里,您尝试检查类是否正在使用Operations层中的正确类,按正确的顺序执行正确的操作。为此,您应该使用模拟,并使用模拟的verify
操作。
假设您有这类课程:
public class API_A {
private OperationA op;
public API_A(OperationA op) {
this.op = op;
}
public int multiplyTwice(int a, int b, int c) {
int x = op.multiply(a, b);
int y = op.multiply(x, c);
return y;
}
}
检查它是否符合您的预期(使用Mockito语法)编写测试,例如:
public class API_A_Test {
@Test
public void testMultiplyTwiceMultipliesTheFirstNumberByTheSecondAndThenByTheThird() {
OperationA op = mock(OperationA.class);
when(op.multiply(12, -5)).thenReturn(60);
API_A api = new API_A(op);
api.multiply(12, -5, 0);
verify(op).multiply(12, -5);
verify(op).multiply(-60, 0);
}
}
答案 2 :(得分:1)
一般来说,您似乎需要两层测试。
首先,您需要测试基本构建块 - 操作。例如,您应该使用有效令牌,使用无效令牌和authenticateUserOperation
来测试NULL
。
一旦您测试了所有操作,您就可以继续测试API的逻辑。这里的想法不是将测试代码加倍,而是仅测试 业务逻辑。这可以通过具有已知行为的mocking或injecting操作来实现,并检查API如何处理它们。
例如,您可以创建一个总是失败的模拟authenticateUserOperation
,并在使用它时测试viewBookApi
返回NULL
(或抛出异常)。这样,您只测试API如何处理操作结果而不重复测试。
答案 3 :(得分:1)
据我了解您的第一个问题,您无法通过单元测试(例如您标记为'它)来解决此问题。也许您必须考虑使用Pairwise testing来创建测试逻辑。这将支持对运营和运营之间的交互的依赖。的API 。
在涉及DI之后,将错误对象注入API ,这使得您的单元测试更容易一些。由于组件松散耦合,您可以使用虚拟模拟类来充当所需组件。通过这样做 - 您的代码将非常灵活。也许这个图表可以帮助你:
但我不认为这是你正在寻找的唯一案例。因此,我可以建议您在测试代码中包含Decorator design pattern,并通过它的概念为API-Operations关系动态地附加测试对象的其他职责。
答案 4 :(得分:1)
你应该在这里进行两组测试。单元测试和集成测试
1)单元测试 - 应为每个操作方法编写这组测试,以确保特定方法按预期工作。现在方法可以有不同的路径(if else / exception)。您应该编写多个单元测试来验证每个路径。由于它是一个单元测试,你应该模拟所有外部调用(调用发生在其他类中),这将确保你在该函数中完成的所有事情都表现正常。由于你在api类中没有任何逻辑,所以不需要为它们编写单元测试
2)集成测试 - 编写集成测试以进行实际的api调用。这些测试将验证您的端到端场景(api,操作)。
如果你有任何持久性/存储库类,你也应该为它们编写单独的测试。