如何测试仅将调用委托给 AR 实体的聚合根方法?
How to test methods of Aggregate Root which just delegates their call to AR's entity?
举一个具体的例子,我有 PromoCode
聚合根,它由 PromoCodeUsage
实体组成,仅由该 AR 控制,因此 AR 上的一些方法只是委托给该实体,喜欢:
public function useFor(Order $order): void
{
$this->promoCodeUsage->useFor($order);
}
其中一些是部分委托的,例如:
public function applyFor(Order $order): void
{
if (!$this->published) {
throw new NotPublishedPromoCodeCanNotBeApplied();
}
$this->promoCodeUsage->applyFor($order);
}
我的测试套件完全涵盖了所有 PromoCode
行为,包括 PromoCodeUsage
功能,因为在那个迭代中没有 PromoCodeUsage
并且所有逻辑都混合在 PromoCode
中。然后我将其中的一些逻辑重构为 PromoCodeUsage
。这个 PromoCode
的测试套件有很多测试,我很高兴我也可以拆分它(但即使在拆分实体后它也能很好地工作)。所以我创建了另一个测试套件 (PromoCodeUsageTest
),其中我从 PromoCode 中移动了部分测试。
但是 PromoCodeUsageTest
正在通过 PromoCode
的行为测试 PromoCodeUsage
实体,就像在拆分之前的原始测试中一样。他们没有直接接触 PromoCodeUsage
。现在我有 PromoCodeTest
套件:
和 PromoCodeUsageTest 套件:
但有点奇怪,1) 在 PromoCodeTest
中我省略了一些测试(在其他地方)和 2) 在 PromoCodeUsageTest
中我实际上没有触及 PromoCodeUsage
实体。 3) 我使用 Roy Osherove 的测试命名模板,我不知道我应该在测试名称中使用什么方法名称 - 来自 PromoCode 还是 PromoCodeUsage?在我的例子中,它们是相同的,但它们可能不同,而且这个想法很糟糕。
如果我重写 PromoCodeUsageTest
s 以直接测试 PromoCodeUsage
实体,我最终会在 PromoCode
上得到一些未覆盖的方法(这些方法只是委托给 PromoCodeUsage
)。所以这让我回到了通过 PromoCode
AR.
测试 PromoCodeUsage
的方法
Bob 大叔(和其他人)说测试行为是很好的做法,而不是 API。我的方法是否符合这一点?
因为我觉得我的方法有点味道,你呢?如何做得更好?
您考虑测试行为是正确的。我假设聚合的所有行为都通过聚合根公开,因此通过根进行测试是有意义的。我只是建议您命名您的测试来描述他们正在测试的行为。不要在测试名称中使用方法名称,因为这些名称可能会更改 - 这会将您的测试名称与生产代码的内部实现联系起来。
如果测试 class 变得非常大,将其分解成更小的 class 是有意义的 - 没有规定测试和生产之间必须有 1:1 关系classes。但是,这可能表明您的 class,在这种情况下,您的聚合可能有太多责任,可能被分解成更小的部分。
我倾向于将聚合视为状态机并相应地对其进行测试。
重要的不是测试在哪个测试文件中,而是根据起始状态和促销代码的类型测试 PromoCode
聚合的所有可能结果状态 usage/application正在做。
当然,这可能需要深入了解聚合的内部,了解相关实体。例如,如果您更愿意放入不同的测试 class 所有其断言查看 PromoCodeUsage
的测试,那么很好,只要测试名称反映域而不是一些技术细节。
举一个具体的例子,我有 PromoCode
聚合根,它由 PromoCodeUsage
实体组成,仅由该 AR 控制,因此 AR 上的一些方法只是委托给该实体,喜欢:
public function useFor(Order $order): void
{
$this->promoCodeUsage->useFor($order);
}
其中一些是部分委托的,例如:
public function applyFor(Order $order): void
{
if (!$this->published) {
throw new NotPublishedPromoCodeCanNotBeApplied();
}
$this->promoCodeUsage->applyFor($order);
}
我的测试套件完全涵盖了所有 PromoCode
行为,包括 PromoCodeUsage
功能,因为在那个迭代中没有 PromoCodeUsage
并且所有逻辑都混合在 PromoCode
中。然后我将其中的一些逻辑重构为 PromoCodeUsage
。这个 PromoCode
的测试套件有很多测试,我很高兴我也可以拆分它(但即使在拆分实体后它也能很好地工作)。所以我创建了另一个测试套件 (PromoCodeUsageTest
),其中我从 PromoCode 中移动了部分测试。
但是 PromoCodeUsageTest
正在通过 PromoCode
的行为测试 PromoCodeUsage
实体,就像在拆分之前的原始测试中一样。他们没有直接接触 PromoCodeUsage
。现在我有 PromoCodeTest
套件:
但有点奇怪,1) 在 PromoCodeTest
中我省略了一些测试(在其他地方)和 2) 在 PromoCodeUsageTest
中我实际上没有触及 PromoCodeUsage
实体。 3) 我使用 Roy Osherove 的测试命名模板,我不知道我应该在测试名称中使用什么方法名称 - 来自 PromoCode 还是 PromoCodeUsage?在我的例子中,它们是相同的,但它们可能不同,而且这个想法很糟糕。
如果我重写 PromoCodeUsageTest
s 以直接测试 PromoCodeUsage
实体,我最终会在 PromoCode
上得到一些未覆盖的方法(这些方法只是委托给 PromoCodeUsage
)。所以这让我回到了通过 PromoCode
AR.
PromoCodeUsage
的方法
Bob 大叔(和其他人)说测试行为是很好的做法,而不是 API。我的方法是否符合这一点?
因为我觉得我的方法有点味道,你呢?如何做得更好?
您考虑测试行为是正确的。我假设聚合的所有行为都通过聚合根公开,因此通过根进行测试是有意义的。我只是建议您命名您的测试来描述他们正在测试的行为。不要在测试名称中使用方法名称,因为这些名称可能会更改 - 这会将您的测试名称与生产代码的内部实现联系起来。
如果测试 class 变得非常大,将其分解成更小的 class 是有意义的 - 没有规定测试和生产之间必须有 1:1 关系classes。但是,这可能表明您的 class,在这种情况下,您的聚合可能有太多责任,可能被分解成更小的部分。
我倾向于将聚合视为状态机并相应地对其进行测试。
重要的不是测试在哪个测试文件中,而是根据起始状态和促销代码的类型测试 PromoCode
聚合的所有可能结果状态 usage/application正在做。
当然,这可能需要深入了解聚合的内部,了解相关实体。例如,如果您更愿意放入不同的测试 class 所有其断言查看 PromoCodeUsage
的测试,那么很好,只要测试名称反映域而不是一些技术细节。