单元测试:具体测试和测试控制流程

发布于 2024-11-18 17:49:32 字数 758 浏览 4 评论 0原文

我对单元测试和一般测试相当陌生。我正在使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

枕头说它不想醒 2024-11-25 17:49:32

Paul 的答案涉及测试方法范围和断言,但您的问题代码暗示的一件事是,如果返回的对象具有值 X,则您将测试 A,但如果返回的对象具有值 Y,则测试 B。换句话说,您的测试将期望多个值并测试根据实际得到的东西不同。

一般来说,如果您的每个测试都有一个单一的、已知的、正确的值,您将成为一个更快乐的测试人员。您可以通过使用固定的已知测试数据(通常是在测试本身内部进行设置)来实现这一点。以下是三种常见路径:

  • 用所有测试使用的一组固定数据填充数据库。随着您添加更多测试和功能,这种情况将会不断发展,并且随着事情的发展,保持其最新状态可能会变得很麻烦。如果您有修改数据的测试,则需要在每次测试后重置数据或回滚更改。
  • 为每个测试创建一个简化的数据集。在 setUp() 期间,测试会删除数据库并使用其特定数据集重新创建数据库。它可以使最初编写测试变得更容易,但您仍然必须随着对象的发展而更新数据集。这也可能会使运行测试花费更长的时间。
  • 当不直接测试这些 DAO 时,请对数据访问对象使用模拟。这允许您在测试中准确指定应返回哪些数据以及何时返回。由于您没有测试 DAO 代码,因此可以模拟它。这使得测试可以快速运行,并且意味着您不需要管理数据集。但是,您仍然需要管理模拟数据并编写模拟代码。根据您的喜好,有许多模拟框架,包括 PHPUnit 自己的内置框架。

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:

  • Fill a database with a set of fixed data used by all tests. This will evolve as you add more tests and functionality, and it can become cumbersome to keep it up-to-date as things move. If you have tests that modify the data, you either need to reset the data after each test or rollback the changes.
  • Create a streamlined data set for each test. During 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.
  • Use mocks for your data access objects when not testing those DAOs directly. This allows you to specify in the test exactly what data should be returned and when. Since you're not testing the DAO code, it's okay to mock it out. This makes the tests run quickly and means you don't need to manage data sets. You still have to manage the mock data, however, and write the mocking code. There are many mocking frameworks depending on your preference, including PHPUnit's own built-in one.
看春风乍起 2024-11-25 17:49:32

在测试用例中拥有一些控制流是可以的,但一般来说,要了解如果单元测试是不相交的,即它们各自测试不同的事物,那么它们的效果会最好。这样做效果很好的原因是,当您的测试用例失败时,您可以从失败的测试用例中准确地看到失败的原因,而不是需要深入到更大的测试用例中来查看出了什么问题。通常的指标是每个单元测试用例的单个断言。也就是说,每条规则都有例外,这当然是其中之一。在测试用例中包含几个断言不一定有问题,特别是当测试用例场景的设置/拆卸特别困难时。然而,您想要避免的真正代码味道是您有一个“测试”来测试所有代码路径的情况。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文