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

时间:2011-02-14 00:25:48

标签: php unit-testing phpunit accessor getter-setter

考虑以下方法:

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

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

假设,将来可能会将它们变得更复杂:

  • 您如何为这些方法编写单元测试?
  • 只是一种测试方法?
  • 我应该跳过这些测试吗?
  • 代码覆盖怎么样?
  • @covers注释怎么样?
  • 也许在抽象测试用例中实现一些通用的测试方法?

(我使用Netbeans 7)

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

qoute from the comment of Sebastian Bergman's blog

  

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

那么,代码覆盖率呢?

3 个答案:

答案 0 :(得分:8)

如果你做TDD,你应该为getter和setter写一个测试。太。不要 编写一行代码而不进行测试 - 即使你的代码是 非常简单。

它是一种宗教战争,使用串联的吸气剂和制定者 您的测试或通过使用访问受保护的类成员来隔离每个 您的单元测试框架功能。作为我喜欢的黑盒测试仪 将我的单元测试代码绑定到公共api而不是绑定到 具体实施细节。我期待改变。我想鼓励 开发人员重构现有代码。并且班级内部不应该 影响“外部代码”(在这种情况下进行单元测试)。我不想打破 单位测试当内部变化时,我希望它们在公众时打破 api更改或行为更改时。好的,好的,如果发生故障的单位 测试不要指向唯一的问题来源。我有 查看getter和setter来找出是什么导致了 问题。大部分时间你的吸气剂非常简单(少于5行 代码:例如返回和带有异常的可选null检查)。所以 首先检查这个并不是什么大问题,也不耗费时间。并检查 安装者的快乐路径大部分时间只是一点点 复杂的(即使你有一些验证检查)。

尝试隔离您的测试用例 - 为SUT编写测试(主题为 test)验证其正确性,而无需其他方法 (除了上面的例子)。你越孤立测试,你的越多 测试发现问题。

根据您的测试策略,您可能只想覆盖快乐路径 (务实的程序员)。或者也是悲伤的。我更愿意涵盖所有 执行路线。当我想我发现所有的执行模式时,我会检查 代码覆盖以识别死代码(不识别是否有代码) 揭露执行路径 - 100%代码覆盖率是一个误导性指标。)

黑盒测试者最好在严格模式下使用phpunit 并使用@covers隐藏抵押品保险。

当您编写单元测试时,A类的测试应该独立于B类执行。因此,A类的单元测试不应该调用/覆盖B类的方法。

如果要识别过时的getter / setter和其他“死”方法(生产代码不使用),请使用静态代码分析。您感兴趣的指标称为“方法级别的传入耦合(MethodCa)”。遗憾的是,此指标(ca)在PHP Depend中的方法级别不可用(请参阅:http://pdepend.org/documentation/software-metrics/index.htmlhttp://pdepend.org/documentation/software-metrics/afferent-coupling.html)。如果您真的需要它,请随意将其贡献给PHP Depend。从同一个类中排除调用的选项有助于在没有“附带”调用的情况下获得结果。如果你确定一个“死方法”,试着弄清楚它是否意味着在不久的将来使用(对于另一个具有@depricated注释的方法),否则将其删除。如果它仅在同一个类中使用,请将其设为privat / protected。不要将此规则应用于库代码。

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

进一步阅读: - 书:实用程序员 - 书:清洁代码

答案 1 :(得分:6)

好问题,

我通常会尝试不直接测试getter和setter,因为我看到只测试实际上某些东西的方法会带来更大的好处。

特别是当不使用TDD时,这有一个额外的好处,就是向我展示我不会在我的单元测试中使用的setter,向我显示我的测试不完整或者没有使用/需要setter。 “如果我可以在不使用该setter的情况下执行所有”真实“代码,为什么会这样。”

当使用流利的setter时,我有时会写一个测试来检查setter的'fluent'部分,但通常会在其他测试中介绍。

回答你的清单:

  • 只是一种测试方法?

这是我最不喜欢的选择。全部或全部。只测试一个并不容易让其他人理解并看起来“随机”或需要以某种方式记录。

评论后编辑:

是的,对于“琐碎的”获取/设置测试,我只使用每个属性可能的一个方法,具体取决于案例,甚至只有一个方法用于整个类(对于具有许多getter和setters我不想写/维护很多测试)

  • 您如何为这些方法编写单元测试?
  • 我应该跳过这些测试吗?

我不会跳过它们。 也许吸气剂取决于你拥有多少(我倾向于只写我真正需要的吸气剂)但完全覆盖课程的任务不应该因为吸气剂而失败。

  • 代码覆盖率怎么样?
  • @covers注释怎么样?

使用@covers我的看法总是“在任何地方使用它或根本不使用它”。混合两种“样式”的测试会消除注释的一些好处,并且看起来“未完成”给我。

  • 也许在抽象测试用例中实现一些通用的测试方法?

对于像价值对象这样可以很好地工作的东西。一旦传入具有类型提示的对象/数组,它可能会破坏(或变得更复杂),但我原本更喜欢它而不是为500个getter和setter编写手动测试。

答案 2 :(得分:3)

这是一个常见的问题,但奇怪的是在SO上找不到傻瓜。

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

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

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

[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));
}