我需要一个单元测试来确保我正确地累积假期时间。但是假期时间是根据业务规则累积的,如果规则改变,那么单元测试就会中断。
这可以接受吗?我是否应该通过一个方法公开规则,然后从我的代码和测试中调用该方法以确保单元测试不那么脆弱?
我的问题是:对可能发生变化的业务规则进行单元测试的正确方法是什么?
我需要一个单元测试来确保我正确地累积假期时间。但是假期时间是根据业务规则累积的,如果规则改变,那么单元测试就会中断。
这可以接受吗?我是否应该通过一个方法公开规则,然后从我的代码和测试中调用该方法以确保单元测试不那么脆弱?
我的问题是:对可能发生变化的业务规则进行单元测试的正确方法是什么?
您的测试应确保代码正确遵守业务规则。因此,我不会编写绕过业务规则或依赖业务规则代码本身的测试。相反,当业务规则发生变化时,我会首先更改测试以反映新的业务规则,然后当我的代码不再通过测试时,去修复代码以使其通过测试。
您不希望发生的事情是编写测试,以便当您更改相同业务规则的应用方式时,您的测试会中断。这说起来容易做起来难,但本质上你想要测试的是实现规则所需的最低要求,而不是太狭隘地规定代码是如何实现的。但是,如果规则的结果不同,那么您应该先更改测试,然后再更改与之匹配的代码。
您也不希望测试与特定数据耦合。假设在进行税收计算时,您的测试不应该假设被测类使用 5% 作为税收。相反,您应该编写测试以提供税收百分比,然后检查计算是否正确。据推测,您将有一系列值进行测试,以确保也捕获超出范围的值。这样做的一个结果是您将拥有更好的设计,因为这将帮助您避免硬编码值并使您的生产代码更灵活地适应数据的变化。
我有类似的设置 - 但是我的业务规则已编译但具有可配置选项(您的可能不同)。当一个业务规则改变了它运行的核心方式时,我的单元测试就会中断。这实际上是意料之中的——而且很好!这意味着我可以隔离整个系统中的任何意外涟漪,并更新测试以匹配更改。
如果您的规则是外部的(某种脚本语言或数据库存储过程),那么您需要将它们包装在集成测试中并连接集成测试以自动执行。虽然不再是单元测试,但集成测试也相当重要,它将以与单元测试相同的方式帮助您防止由于业务规则更改而导致的意外涟漪。
听起来您有业务规则,然后您有这些业务规则的客户。如果这两者可以独立变化,那么相应地设计您的 API 将是明智之举。
如前所述,您的问题听起来可以通过适当使用策略模式来解决。策略代表您的业务规则,因此您可以在纯上下文中对这些规则进行单元测试,而无需担心客户端。
当业务规则发生变化时,简单地保留旧策略(以防以后再次需要它)并编写(和单元测试)一个代表新业务规则的全新策略可能更有意义。
当您完全完成新策略后,您可以替换客户端中的策略。
在对客户端进行单元测试时,您应该针对测试双重策略(如 Mock 或 Stub)执行此操作,以验证客户端是否与策略正确交互,而不依赖于任何特定的策略实现。
通过这种方式,您可以清晰地分离关注点,并使单元测试保持可维护性。您还遵守开放/封闭原则。
如果规则被更改,测试失败听起来是正确的——这是意料之中的。
如果您在测试本身中使用数据夹具而不是硬编码值,您可以更轻松地在未来维护测试。例如,如果你的方法应该返回 foo,你可以在夹具中拥有它。当您更改业务逻辑时,您只需更改夹具,您不必通过单元测试本身。
当然,这在很大程度上取决于您正在测试的逻辑类型,并且可能并不总是适用。
我认为这是一个不正确的问题业务规则和单元测试处于不同的抽象级别。业务规则处于最高抽象级别(业务建模),但单元测试用于测试处于最低抽象级别的代码单元,因此您不能使用单元测试来验证或验证业务规则。 另外一个业务规则经过分析和设计后可能会影响到几个代码单元,所以你不能再用单元测试来验证或验证一个业务规则。我认为您可以使用测试用例或 BDD 场景来验证和验证业务规则。