单元测试访问器(getter和setter)

时间:2022-09-24 21:32:15

Given the following methods:

鉴于以下方法:

public function setFoo($foo) {
    $this->_foo = $foo;
    return $this;
}

public function getFoo() {
    return $this->_foo;
}

Assuming, they may be changed to be more complex in the future:

假设它们在未来会变得更加复杂:

  • How would you write unit tests for those methods?
  • 如何为这些方法编写单元测试?
  • Just one test method?
  • 只是一个测试方法?
  • Should I skip those tests?
  • 我应该跳过这些测试吗?
  • What about code coverage?
  • 代码覆盖呢?
  • How about @covers annotation?
  • @covers注释呢?
  • Maybe some universal test method to implement in the abstract test case?
  • 在抽象的测试用例中实现什么通用的测试方法?

(I use Netbeans 7)

(我使用Netbeans 7)

This seems like a waste of time, but I wouldn't mind if IDE would generate those test methods automatically.

这似乎是浪费时间,但我不介意IDE自动生成这些测试方法。

To qoute from the comment of Sebastian Bergman's blog:

来自塞巴斯蒂安·伯格曼博客评论的qoute:

(it's like testing getters and setters -- fail!). In any case, if they were to fail; wouldn't the methods that depend on on them fail?

(这就像测试getter和setter一样——失败!)无论如何,如果他们失败了;依赖于它们的方法不会失败吗?

So, what about the code coverage?

那么,代码覆盖率呢?

3 个解决方案

#1


6  

Good Question,

好问题,

i usually try not to test getters&setters directly since i see a greater benefit in testing only the methods that actually do something.

我通常尽量不直接测试getters&setters,因为我发现只测试实际执行的方法有更大的好处。

Especially when not using TDD this has the added benefit of showing me setters that i don't use in my unittests showing me that ether my tests are incomplete or that the setter is not used/needed. "If i can execute all the "real" code without using that setter why is it there."

特别是当不使用TDD时,这有一个额外的好处,就是可以向我显示我在unittests中不使用的setter,从而显示我的测试是不完整的,或者setter是不使用/不需要的。如果我可以执行所有的“真正的”代码而不使用setter,为什么会有它呢?

When using fluent setter i sometimes write a test checking the 'fluent' part of the setters but usually that is covered in other tests.

在使用fluent setter时,我有时会编写一个测试来检查setter的“fluent”部分,但通常在其他测试中会涉及到。

To answer your list:

回答你的列表:

  • Just one test method?
  • 只是一个测试方法?

That is my least favorite option. All or none. Testing only one is not easy for other people to understand and looks 'random' or needs to be documented in a way.

这是我最不喜欢的选择。全部或没有。对其他人来说,只测试一个并不容易理解,而且看起来“随机”,或者需要以某种方式进行记录。

Edit after comment:

编辑后的评论:

Yes, for "trivial" get/set testing I'd only use one method per property maybe depending on the case even only one method for the whole class (for value objects with many getters and setters I don't want to write/maintain many tests)

是的,对于“平凡”的get/set测试,我每个属性只使用一个方法,可能取决于情况,甚至整个类只有一个方法(对于具有许多getter和setter的值对象,我不想编写/维护多个测试)

  • How would you write unit tests for those methods?
  • 如何为这些方法编写单元测试?
  • Should I skip those tests?
  • 我应该跳过这些测试吗?

I wouldn't skip them. Maybe the getters depending on how many you have (i tend to write only getters i actually need) but the task of having a class completely covered shouldn't fail because of getters.

我不会跳过它们。可能getters取决于你有多少个(我倾向于只写我实际需要的getter),但是完全覆盖一个类的任务不应该因为getter而失败。

  • What about code coverage?
  • 代码覆盖呢?
  • How about @covers annotation?
  • @covers注释呢?

With @covers my take is always "use it everywhere or don't use it at all". Mixing the two 'styles' of testing takes away some of the benefits of the annotation and looks 'unfinished' to me.

对于@cover,我的理解总是“在任何地方使用它,或者根本不使用它”。混合使用这两种“风格”的测试会减少注释的一些好处,在我看来是“未完成”的。

  • Maybe some universal test method to implement in the abstract test case?
  • 在抽象的测试用例中实现什么通用的测试方法?

For something like value objects that could work nicely. It might break (or gets more complicated) once you pass in objects / array with type hinting but I'd presonally prefer it over writing manual tests for 500 getters and setters.

对于像value这样可以很好地工作的对象。一旦您传入带有类型提示的对象/数组,它可能会被破坏(或者变得更复杂),但与为500个getter和setter编写手工测试相比,我更喜欢它。

#2


7  

If you do TDD you should write a test for getter and setter. too. Do not write a single line of code without a test for it - even if your code is very simple.

如果您执行TDD,您应该为getter和setter编写一个测试。了。不要在没有测试的情况下编写一行代码——即使您的代码非常简单。

Its a kind of religious war to use a tandem of getter and setter for your test or to isolate each by accessing protected class members using your unit test framework capabilities. As a black box tester i prefer to tie my unit test code to the public api instead of tie it to the concrete implementation details. I expect change. I want to encourage the developers to refactor existing code. And class internals should not effect "external code" (unit tests in this case). I don want to break unit tests when internals change, i want them to break when the public api changes or when behavior changes. Ok, ok, in case of a failing unit test do not pin-point to the one-and-only source of problem. I do have to look in the getter AND the setter to figure out what caused the problem. Most of the time your getter is very simple (less then 5 lines of code: e.g. a return and an optional null-check with an exception). So checking this first is no big deal and not time consuming. And checking the happy path of the setter is most of the time only a little more complex (even if you have some validation checks).

使用一系列的getter和setter进行测试,或者通过使用单元测试框架功能访问受保护的类成员来隔离每个成员,这是一种宗教战争。作为一个黑盒测试人员,我更喜欢将单元测试代码绑定到公共api,而不是将它绑定到具体的实现细节。我期待改变。我希望鼓励开发人员重构现有代码。类内部不应该影响“外部代码”(在本例中是单元测试)。我不想在内部变更时破坏单元测试,我想在公共api变更或行为变更时破坏单元测试。好的,如果单元测试失败了,不要指向唯一的问题源。我必须在getter和setter中查找导致问题的原因。大多数时候,您的getter非常简单(少于5行代码:例如返回和一个异常的可选的空检查)。所以首先检查这个并不重要,也不浪费时间。检查setter的愉快路径大部分时间都只是稍微复杂一点(即使您有一些验证检查)。

Try to isolate your test cases - write a test for a SUT (Subject under test) that validates its correctness without reley on other methods (except my example above). The more you isolate the test, the more your tests spot the problem.

尝试隔离您的测试用例——编写一个测试SUT(被测试的对象),它可以在没有其他方法的情况下验证其正确性(除了上面的例子)。您越是隔离测试,您的测试就越能发现问题。

Depending on your test strategy you may be want to cover happy path only (pragmatic programmer). Or sad pathes, too. I prefer to cover all execution pathes. When i think i discovered all execution pathes i check code coverage to identify dead code (not to identify if there are uncovered execution pathes - 100% code coverage is a missleading indicator).

根据您的测试策略,您可能希望只覆盖“快乐路径”(实用程序员)。或悲伤道。我倾向于覆盖所有执行路径。当我认为我发现了所有执行路径时,我检查代码覆盖率来识别死代码(不确定是否有未发现的执行路径——100%的代码覆盖率是一个错误的指示器)。

It is best practice for black box testers to use phpunit in strict mode and to use @covers to hide collateral coverage.

黑盒测试人员最好在严格的模式下使用phpunit,并使用@cover隐藏附属覆盖率。

When you write unit test your test on class A should be executed independent from class B. So your unit tests for class A should not call / cover method of class B.

当你写单元测试时,你在A类上的测试应该独立于B类来执行,所以你在A类上的单元测试不应该调用B类的方法。

If you want to identify obsolete getter/setter and other "dead" methods (which are not used by production code) use static code analysis for that. The metric you are interested in is called "Afferent coupling at method level (MethodCa)". Unfortunately this metric (ca) is not available at method-level in PHP Depend (see: http://pdepend.org/documentation/software-metrics/index.html and http://pdepend.org/documentation/software-metrics/afferent-coupling.html). If you realy need it, feel free to contribute it to PHP Depend. An option to exclude calls from the same class would be helpful to get a result without "collateral" calls. If you identify a "dead method" try to figure out if it is meant to be used in near future (the counterpart for an other method that has a @depricated annotation) else remove it. In case it is used in the same class only, make it privat / protected. Do not apply this rule to library code.

如果您想识别过时的getter/setter和其他“死”方法(不被生产代码使用),请使用静态代码分析。您感兴趣的度量称为“方法级的传入耦合(MethodCa)”。不幸的是,这个度量(ca)在PHP Depend的方法级别上是不可用的(参见:http://pdepend.org/documentation/softw-metrics/index.html和http://pdepend.org/documentation/softw-metrics/afferent -coupling.html)。如果您确实需要它,请将它贡献给PHP依赖。排除来自同一个类的调用的选项将有助于在没有“附带”调用的情况下获得结果。如果您识别了一个“死方法”,请尝试找出它是否打算在不久的将来被使用(另一个具有@德普化注释的方法的对应方法),那么请删除它。如果只在同一类中使用,请将其设置为私有/受保护。不要将此规则应用于库代码。

Plan B: If you have acceptance tests (integration test, regression test, etc.) you can run that test without running unit tests at the same time and without phpunits strict mode. This can result in a very similar code coverage result as if you had analysed your production code. But in most cases your non-unit tests are not as strong as your production code is. It depends on your discipline if this plan B is "equal enought" to production code to get a meaningful result.

计划B:如果您有验收测试(集成测试、回归测试等),您可以在不同时运行单元测试和没有phpunits严格模式的情况下运行该测试。这可能导致非常类似的代码覆盖率结果,就像您分析了您的产品代码一样。但是在大多数情况下,您的非单元测试不如您的产品代码强大。如果这个计划B与生产代码“相等”以获得有意义的结果,这取决于您的规程。

Further reading: - Book: Pragmatic Programmer - Book: Clean Code

进一步阅读:-书:实用程序员-书:干净的代码

#3


3  

This is a common question but strangely can't find a dupe on SO.

这是一个常见的问题,但奇怪的是却找不到这样的问题。

You could write unit tests for accessors but the majority of practioners do not. i.e. if the accessors do not have any custom logic, I would not write unit tests to verify if field access works. Instead I would rely on the consumers of these accessors to ensure that the accessors work. e.g. If getFoo and setFoo don't work, the callers of these method should break. So by writing unit tests for the calling methods, the accessors get verified.

您可以为访问器编写单元测试,但大多数实践者都不这样做。例如,如果访问器没有任何自定义逻辑,我就不会编写单元测试来验证字段访问是否有效。相反,我将依赖这些访问器的使用者来确保访问器工作。如果getFoo和setFoo不工作,这些方法的调用者应该会中断。因此,通过为调用方法编写单元测试,访问器将得到验证。

This also means that code coverage should not be a problem. If you find accessors that are not covered after all test suites are run, maybe they are redundant / unused. Delete them.

这也意味着代码覆盖率不应该成为问题。如果在运行所有测试套件之后发现访问器没有覆盖,那么它们可能是冗余的/未使用的。删除它们。

Try to write a test that illustrates a scenario where a client will use that accessor. e.g. The below snippet shows how the Tooltip (property) for the Pause Button toggles based on its current mode.

尝试编写一个测试来说明客户端将使用该访问器的场景。下面的代码片段显示了暂停按钮的工具提示(属性)是如何根据当前模式切换的。

[Test]
public void UpdatesTogglePauseTooltipBasedOnState()
{
    Assert.That(_mainViewModel.TogglePauseTooltip, Is.EqualTo(Strings.Main_PauseAllBeacons));

    _mainViewModel.TogglePauseCommand.Execute(null);
    Assert.That(_mainViewModel.TogglePauseTooltip, Is.EqualTo(Strings.Main_ResumeAllBeacons));

    _mainViewModel.TogglePauseCommand.Execute(null);
    Assert.That(_mainViewModel.TogglePauseTooltip, Is.EqualTo(Strings.Main_PauseAllBeacons));
}

#1


6  

Good Question,

好问题,

i usually try not to test getters&setters directly since i see a greater benefit in testing only the methods that actually do something.

我通常尽量不直接测试getters&setters,因为我发现只测试实际执行的方法有更大的好处。

Especially when not using TDD this has the added benefit of showing me setters that i don't use in my unittests showing me that ether my tests are incomplete or that the setter is not used/needed. "If i can execute all the "real" code without using that setter why is it there."

特别是当不使用TDD时,这有一个额外的好处,就是可以向我显示我在unittests中不使用的setter,从而显示我的测试是不完整的,或者setter是不使用/不需要的。如果我可以执行所有的“真正的”代码而不使用setter,为什么会有它呢?

When using fluent setter i sometimes write a test checking the 'fluent' part of the setters but usually that is covered in other tests.

在使用fluent setter时,我有时会编写一个测试来检查setter的“fluent”部分,但通常在其他测试中会涉及到。

To answer your list:

回答你的列表:

  • Just one test method?
  • 只是一个测试方法?

That is my least favorite option. All or none. Testing only one is not easy for other people to understand and looks 'random' or needs to be documented in a way.

这是我最不喜欢的选择。全部或没有。对其他人来说,只测试一个并不容易理解,而且看起来“随机”,或者需要以某种方式进行记录。

Edit after comment:

编辑后的评论:

Yes, for "trivial" get/set testing I'd only use one method per property maybe depending on the case even only one method for the whole class (for value objects with many getters and setters I don't want to write/maintain many tests)

是的,对于“平凡”的get/set测试,我每个属性只使用一个方法,可能取决于情况,甚至整个类只有一个方法(对于具有许多getter和setter的值对象,我不想编写/维护多个测试)

  • How would you write unit tests for those methods?
  • 如何为这些方法编写单元测试?
  • Should I skip those tests?
  • 我应该跳过这些测试吗?

I wouldn't skip them. Maybe the getters depending on how many you have (i tend to write only getters i actually need) but the task of having a class completely covered shouldn't fail because of getters.

我不会跳过它们。可能getters取决于你有多少个(我倾向于只写我实际需要的getter),但是完全覆盖一个类的任务不应该因为getter而失败。

  • What about code coverage?
  • 代码覆盖呢?
  • How about @covers annotation?
  • @covers注释呢?

With @covers my take is always "use it everywhere or don't use it at all". Mixing the two 'styles' of testing takes away some of the benefits of the annotation and looks 'unfinished' to me.

对于@cover,我的理解总是“在任何地方使用它,或者根本不使用它”。混合使用这两种“风格”的测试会减少注释的一些好处,在我看来是“未完成”的。

  • Maybe some universal test method to implement in the abstract test case?
  • 在抽象的测试用例中实现什么通用的测试方法?

For something like value objects that could work nicely. It might break (or gets more complicated) once you pass in objects / array with type hinting but I'd presonally prefer it over writing manual tests for 500 getters and setters.

对于像value这样可以很好地工作的对象。一旦您传入带有类型提示的对象/数组,它可能会被破坏(或者变得更复杂),但与为500个getter和setter编写手工测试相比,我更喜欢它。

#2


7  

If you do TDD you should write a test for getter and setter. too. Do not write a single line of code without a test for it - even if your code is very simple.

如果您执行TDD,您应该为getter和setter编写一个测试。了。不要在没有测试的情况下编写一行代码——即使您的代码非常简单。

Its a kind of religious war to use a tandem of getter and setter for your test or to isolate each by accessing protected class members using your unit test framework capabilities. As a black box tester i prefer to tie my unit test code to the public api instead of tie it to the concrete implementation details. I expect change. I want to encourage the developers to refactor existing code. And class internals should not effect "external code" (unit tests in this case). I don want to break unit tests when internals change, i want them to break when the public api changes or when behavior changes. Ok, ok, in case of a failing unit test do not pin-point to the one-and-only source of problem. I do have to look in the getter AND the setter to figure out what caused the problem. Most of the time your getter is very simple (less then 5 lines of code: e.g. a return and an optional null-check with an exception). So checking this first is no big deal and not time consuming. And checking the happy path of the setter is most of the time only a little more complex (even if you have some validation checks).

使用一系列的getter和setter进行测试,或者通过使用单元测试框架功能访问受保护的类成员来隔离每个成员,这是一种宗教战争。作为一个黑盒测试人员,我更喜欢将单元测试代码绑定到公共api,而不是将它绑定到具体的实现细节。我期待改变。我希望鼓励开发人员重构现有代码。类内部不应该影响“外部代码”(在本例中是单元测试)。我不想在内部变更时破坏单元测试,我想在公共api变更或行为变更时破坏单元测试。好的,如果单元测试失败了,不要指向唯一的问题源。我必须在getter和setter中查找导致问题的原因。大多数时候,您的getter非常简单(少于5行代码:例如返回和一个异常的可选的空检查)。所以首先检查这个并不重要,也不浪费时间。检查setter的愉快路径大部分时间都只是稍微复杂一点(即使您有一些验证检查)。

Try to isolate your test cases - write a test for a SUT (Subject under test) that validates its correctness without reley on other methods (except my example above). The more you isolate the test, the more your tests spot the problem.

尝试隔离您的测试用例——编写一个测试SUT(被测试的对象),它可以在没有其他方法的情况下验证其正确性(除了上面的例子)。您越是隔离测试,您的测试就越能发现问题。

Depending on your test strategy you may be want to cover happy path only (pragmatic programmer). Or sad pathes, too. I prefer to cover all execution pathes. When i think i discovered all execution pathes i check code coverage to identify dead code (not to identify if there are uncovered execution pathes - 100% code coverage is a missleading indicator).

根据您的测试策略,您可能希望只覆盖“快乐路径”(实用程序员)。或悲伤道。我倾向于覆盖所有执行路径。当我认为我发现了所有执行路径时,我检查代码覆盖率来识别死代码(不确定是否有未发现的执行路径——100%的代码覆盖率是一个错误的指示器)。

It is best practice for black box testers to use phpunit in strict mode and to use @covers to hide collateral coverage.

黑盒测试人员最好在严格的模式下使用phpunit,并使用@cover隐藏附属覆盖率。

When you write unit test your test on class A should be executed independent from class B. So your unit tests for class A should not call / cover method of class B.

当你写单元测试时,你在A类上的测试应该独立于B类来执行,所以你在A类上的单元测试不应该调用B类的方法。

If you want to identify obsolete getter/setter and other "dead" methods (which are not used by production code) use static code analysis for that. The metric you are interested in is called "Afferent coupling at method level (MethodCa)". Unfortunately this metric (ca) is not available at method-level in PHP Depend (see: http://pdepend.org/documentation/software-metrics/index.html and http://pdepend.org/documentation/software-metrics/afferent-coupling.html). If you realy need it, feel free to contribute it to PHP Depend. An option to exclude calls from the same class would be helpful to get a result without "collateral" calls. If you identify a "dead method" try to figure out if it is meant to be used in near future (the counterpart for an other method that has a @depricated annotation) else remove it. In case it is used in the same class only, make it privat / protected. Do not apply this rule to library code.

如果您想识别过时的getter/setter和其他“死”方法(不被生产代码使用),请使用静态代码分析。您感兴趣的度量称为“方法级的传入耦合(MethodCa)”。不幸的是,这个度量(ca)在PHP Depend的方法级别上是不可用的(参见:http://pdepend.org/documentation/softw-metrics/index.html和http://pdepend.org/documentation/softw-metrics/afferent -coupling.html)。如果您确实需要它,请将它贡献给PHP依赖。排除来自同一个类的调用的选项将有助于在没有“附带”调用的情况下获得结果。如果您识别了一个“死方法”,请尝试找出它是否打算在不久的将来被使用(另一个具有@德普化注释的方法的对应方法),那么请删除它。如果只在同一类中使用,请将其设置为私有/受保护。不要将此规则应用于库代码。

Plan B: If you have acceptance tests (integration test, regression test, etc.) you can run that test without running unit tests at the same time and without phpunits strict mode. This can result in a very similar code coverage result as if you had analysed your production code. But in most cases your non-unit tests are not as strong as your production code is. It depends on your discipline if this plan B is "equal enought" to production code to get a meaningful result.

计划B:如果您有验收测试(集成测试、回归测试等),您可以在不同时运行单元测试和没有phpunits严格模式的情况下运行该测试。这可能导致非常类似的代码覆盖率结果,就像您分析了您的产品代码一样。但是在大多数情况下,您的非单元测试不如您的产品代码强大。如果这个计划B与生产代码“相等”以获得有意义的结果,这取决于您的规程。

Further reading: - Book: Pragmatic Programmer - Book: Clean Code

进一步阅读:-书:实用程序员-书:干净的代码

#3


3  

This is a common question but strangely can't find a dupe on SO.

这是一个常见的问题,但奇怪的是却找不到这样的问题。

You could write unit tests for accessors but the majority of practioners do not. i.e. if the accessors do not have any custom logic, I would not write unit tests to verify if field access works. Instead I would rely on the consumers of these accessors to ensure that the accessors work. e.g. If getFoo and setFoo don't work, the callers of these method should break. So by writing unit tests for the calling methods, the accessors get verified.

您可以为访问器编写单元测试,但大多数实践者都不这样做。例如,如果访问器没有任何自定义逻辑,我就不会编写单元测试来验证字段访问是否有效。相反,我将依赖这些访问器的使用者来确保访问器工作。如果getFoo和setFoo不工作,这些方法的调用者应该会中断。因此,通过为调用方法编写单元测试,访问器将得到验证。

This also means that code coverage should not be a problem. If you find accessors that are not covered after all test suites are run, maybe they are redundant / unused. Delete them.

这也意味着代码覆盖率不应该成为问题。如果在运行所有测试套件之后发现访问器没有覆盖,那么它们可能是冗余的/未使用的。删除它们。

Try to write a test that illustrates a scenario where a client will use that accessor. e.g. The below snippet shows how the Tooltip (property) for the Pause Button toggles based on its current mode.

尝试编写一个测试来说明客户端将使用该访问器的场景。下面的代码片段显示了暂停按钮的工具提示(属性)是如何根据当前模式切换的。

[Test]
public void UpdatesTogglePauseTooltipBasedOnState()
{
    Assert.That(_mainViewModel.TogglePauseTooltip, Is.EqualTo(Strings.Main_PauseAllBeacons));

    _mainViewModel.TogglePauseCommand.Execute(null);
    Assert.That(_mainViewModel.TogglePauseTooltip, Is.EqualTo(Strings.Main_ResumeAllBeacons));

    _mainViewModel.TogglePauseCommand.Execute(null);
    Assert.That(_mainViewModel.TogglePauseTooltip, Is.EqualTo(Strings.Main_PauseAllBeacons));
}