单元测试:具体测试和测试控制流程
我对单元测试和一般测试相当陌生。我正在使用 phpUnit 进行开发,但由于我的问题更一般/设计问题,实际环境不应该太重要。
我认为,编写尽可能具体的测试用例是一种很好的做法。例如(越晚越好):
assertNotEmpty($myObject); // myObject is not Null
assertInternalType('array', $myObject); // myObject is an array
assertGreaterThan(0, count($myObject)); // myObject actually has entries
如果这是正确的,这是我的问题:
在测试用例中编写一些流程控制是否是一种可接受的做法,如果状态正在测试的对象的依赖于外部源(即数据库) - 或者甚至一般而言?
例如:
if (myObject !== null) {
if (count(myObject) > 0) {
// assert some Business Logic
}
else {
// assert some different Business Logic
}
}
测试用例内的这种流量控制是否可以接受,或者是“代码味道”并且应该被规避?如果可以的话,有什么技巧或做法应该牢记在心吗?
I am quite new to unit testing and testing in general. I am developing with phpUnit, but as my question is more general / a design question, the actual environment shouldn't be of too much importance.
I assume, that it is a good practice, to write your testcases as specific as possible. For example (the later the better):
assertNotEmpty($myObject); // myObject is not Null
assertInternalType('array', $myObject); // myObject is an array
assertGreaterThan(0, count($myObject)); // myObject actually has entries
If that is correct, here is my question:
Is it a accepted practice to write some flow control inside a testcase, if the state of the object one is testing against depends on external sources (i.e. DB) - or even in general?
Like:
if (myObject !== null) {
if (count(myObject) > 0) {
// assert some Business Logic
}
else {
// assert some different Business Logic
}
}
Is this kind of flow control inside a testcase admissible or is a "code smell" and should be circumvented? If it is ok, are there any tips or practices, which should be kept in mind here?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
Paul 的答案涉及测试方法范围和断言,但您的问题代码暗示的一件事是,如果返回的对象具有值 X,则您将测试 A,但如果返回的对象具有值 Y,则测试 B。换句话说,您的测试将期望多个值并测试根据实际得到的东西不同。
一般来说,如果您的每个测试都有一个单一的、已知的、正确的值,您将成为一个更快乐的测试人员。您可以通过使用固定的已知测试数据(通常是在测试本身内部进行设置)来实现这一点。以下是三种常见路径:
setUp()
期间,测试会删除数据库并使用其特定数据集重新创建数据库。它可以使最初编写测试变得更容易,但您仍然必须随着对象的发展而更新数据集。这也可能会使运行测试花费更长的时间。Paul's answer addresses test method scope and assertions, but one thing your question's code implied is that you would test A if the returned object had value X but test B if it had value Y. In other words, your test would expect multiple values and test different things based on what it actually got.
In general, you will be a happier tester if each of your tests has a single, known, correct value. You achieve this by using fixed, known test data, often by setting it up inside the test itself. Here are three common paths:
setUp()
the test drops and recreates the database using its specific data set. It can make writing tests easier initially, but you still must update the datasets as the objects evolve. This can also make running the tests take longer.在测试用例中拥有一些控制流是可以的,但一般来说,要了解如果单元测试是不相交的,即它们各自测试不同的事物,那么它们的效果会最好。这样做效果很好的原因是,当您的测试用例失败时,您可以从失败的测试用例中准确地看到失败的原因,而不是需要深入到更大的测试用例中来查看出了什么问题。通常的指标是每个单元测试用例的单个断言。也就是说,每条规则都有例外,这当然是其中之一。在测试用例中包含几个断言不一定有问题,特别是当测试用例场景的设置/拆卸特别困难时。然而,您想要避免的真正代码味道是您有一个“测试”来测试所有代码路径的情况。
It's okay to have SOME control flow within your testcases, but in general, understand that your unit tests will work out best if they are disjoint, that is, they each test for different things. The reason this works out well is that when your test cases fail, you can see precisely what the failure is from the testcase that failed, as opposed to needing to go deeper inside a larger test case to see what went wrong. The usual metric is a single assertion per unit test case. That said, there are exceptions to every rule, and that's certainly one of them; there's nothing necessarily wrong with having a couple of assertions in your test case, particularly when the setup / teardown of the test case scenario is particularly difficult. However, the real code smell you want to avoid is the situation where you have one "test" which tests all the code paths.