为了测试能力,我们避免 "new" 并使用依赖注入。对象的方法参数中的 "new" 怎么样?

For test-ability, we avoid "new" and use Dependency Injection. What about "new" in an object's method argument?

我很容易理解不是testable/mockable:

public function index(){

    $jedi = new Jedi(); // <-- "new" operator used here

    ...
}

使用依赖注入,我现在很容易理解 IS testable/mockable:

public function index(JediInterface $jedi){

    $jedi->doSomething();

    ...
}

但是方法参数重要吗?考虑下面的这段代码,它是否也很容易testable/mockable?

public function index(JediInterface $jedi){

    $jedi->setLightSaber(new BlueLightsaber()); // <-- "new" operator used in argument

    return $jedi->getLightsaberColor();
}

注: $jedi class 具有方法定义:
public function setLightSaber(LightSaberInterface $saber)

否,因为对象是在方法内部创建的。想象一下,您如何在使用 index() 的不同程序中用 ChainSaw 替换 BlueSaber?

会影响可测性。也许不是立即,但是当你需要测试一些根据军刀颜色而不同的对象时它会(这里它总是蓝色的,你不能改变它)。你可以争辩说,在这个确切的例子中并没有发生,但一般来说,它会发生。

此外,也许将来您想要改变 BlueLightsaber 的工作方式,这样它会在创建时记录一些信息。如果不能mock,测试的时候会写真实的日志。

有很多原因。学习它们的更好和更难的方法是尝试测试受直接对象实例化困扰的遗留代码。

如果 BlueLightSaber 的构造函数绝对没有副作用并且您在不久的将来没有看到光剑的类型发生变化,我会保留您的实现就是这样。添加间接总是会带来额外的复杂性。

至于可测试性,您现在正在测试 Jedi 是否与 BlueLightSaber 一起正常工作(即您正在编写集成测试而不是仅针对 Jedi 的孤立测试),但这本身不一定是问题.虽然当集成测试失败时确实会更难以查明确切的问题,但如果光剑确实是绝地武士的实现细节,那么您最好编写一个集成测试。当孤立的测试对实现细节了解得太多时,它们往往会妨碍未来的重构。

(有关独立测试与集成测试的更多信息 here

还有一点值得一提:您在单个方法调用中将查询(获取光剑颜色)与更改状态(设置光剑)的逻辑混合在一起。这可能会让使用您的代码的开发人员感到困惑,并且通常可以将它们分开以便更好地理解。这个原则叫做Command-Query Separation(CQS)。此外,我倾向于尽可能避免更改输入参数(jedi)的状态,因为它会导致与违反 CQS 相同的令人困惑的错误。