Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/231.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
将同一对象中具有依赖关系的方法重构为更可测试的方法(PHP)_Php_Unit Testing_Tdd_Dependency Injection_Inversion Of Control - Fatal编程技术网

将同一对象中具有依赖关系的方法重构为更可测试的方法(PHP)

将同一对象中具有依赖关系的方法重构为更可测试的方法(PHP),php,unit-testing,tdd,dependency-injection,inversion-of-control,Php,Unit Testing,Tdd,Dependency Injection,Inversion Of Control,我目前在我的类中有一个方法,它必须调用其他方法,一些方法在同一个对象中,另一些方法来自其他对象 class MyClass { public function myMethod() { $var1 = $this->otherMethod1(); $var2 = $this->otherMethod2(); $var3 = $this->otherMethod3(); $otherObject = new Othe

我目前在我的类中有一个方法,它必须调用其他方法,一些方法在同一个对象中,另一些方法来自其他对象


class MyClass
{
    public function myMethod()
    {
        $var1 = $this->otherMethod1();
        $var2 = $this->otherMethod2();
        $var3 = $this->otherMethod3();
        $otherObject = new OtherClass();
        $var4 = $otherObject->someMethod();

        # some processing goes on with these 4 variables
        # then the method returns something else

        return $var5;
    }
}
我对整个TDD游戏还不熟悉,但我认为我所理解的更多可测试代码的关键前提是组合、松散耦合和一些依赖注入/控制反转策略

在这种特殊情况下,我如何将一个方法重构成更可测试的东西

我是否将
$this
对象引用作为参数传递给该方法,以便轻松模拟/存根协作方法?这是推荐的还是过于夸张了?


class MyClass
{
    public function myMethod($self, $other)
    {
        # $self == $this
        $var1 = $self->otherMethod1();
        $var2 = $self->otherMethod2();
        $var3 = $self->otherMethod3();
        $var4 = $other->someMethod();

        # ...

        return $var5;
    }
}

另外,对我来说,依赖关系对于TDD来说是一个非常重要的问题,因为人们必须考虑如何将存根/模拟注入到所述方法中进行测试。大多数TDER是否将DI/IoC作为公共依赖的主要策略?在哪一点上会被夸大?你能给出一些有效的建议吗?

我可能错了,但我的印象是,对象/类对于它们的客户机来说应该是黑盒子,对于它们的测试客户机来说也是如此(我认为封装是我要寻找的术语)。

这些问题很好。。。首先,我要说的是,我根本不了解JS,但我是一名单元测试人员,并处理过这些问题。我首先想指出,如果您不使用JsUnit,它是存在的

我不会太担心您的方法调用同一类中的其他方法。。。这是必然的。我更担心的是另一个对象的创建,这取决于它有多复杂

例如,如果您正在实例化一个通过网络执行各种操作的类,那么对于一个简单的单元测试来说,这个类太重了。您更愿意做的是模拟对该类的依赖,这样您就可以让对象生成您希望从其在网络上的操作中收到的结果,而不会产生在网络上的开销:网络故障、时间等等

传入另一个对象有点混乱。人们通常使用工厂方法来实例化另一个对象。工厂方法可以根据您是否正在测试(通常通过标志)来决定是否实例化真实对象或模拟对象。事实上,您可能希望使另一个对象成为类的成员,并在构造函数中调用工厂,或者在那里决定是否实例化模拟对象或真实对象。在setup函数或测试用例中,您可以对模拟对象设置特殊条件,以便它返回正确的值


另外,只需确保在同一类中对其他函数进行了测试。。。我希望这有帮助

您可以做几件事:

最好的方法是模拟,这里有一个这样的库:

它应该让你只模拟出你想要的特定功能

如果这不起作用,那么下一个最好的方法是提供
method2
的实现,作为
MyClass
类中对象的方法。如果不能直接模拟方法,我发现这是一种更简单的方法:

class MyClass {
  function __construct($method2Impl) {
    $this->method2Impl = $method2Impl;
  }
  function method2() {
    return $this->method2Imple->call();
  }
}
另一个选项是添加一个“正在测试”标志,以便该方法的行为不同。我也不推荐这样做——最终你会有不同的代码路径和它们自己的bug

另一个选择是子类化并覆盖所需的行为。我-真的-不建议这样做,因为您最终会自定义覆盖的mock,使其本身有bug:)


最后,如果您需要模拟一个方法,因为它太复杂了,那么将它移动到自己的对象中并使用组合(基本上使用我上面提到的
method2Impl
技术)可能是一个很好的迹象。

看起来这个类的整个想法并不完全正确。在TDD中,您测试的是类,而不是方法。如果一个方法有它自己的责任并提供它自己的(单独的可测试的)功能,那么它应该被移动到一个单独的类中。否则它只会破坏整个OOP封装。特别是它打破了单一责任原则

在您的情况下,我会将测试的方法提取到另一个类中,并将
$var1
$var2
$var3
$other
作为依赖项注入。
$other
以及测试类所依赖的任何对象都应该被模拟

class TestMyClass extends MyTestFrameworkUnitTestBase{
     function testMyClass()
     {
          $myClass = new MyClass();
          $myClass->setVar1('asdf');
          $myClass->setVar2(23);
          $myClass->setVar3(78);
          $otherMock = getMockForClassOther();
          $myClass->setOther($otherMock);
          $this->assertEquals('result', $myClass->myMethod());
     }
}

我使用的基本规则是:如果我想测试某个东西,我应该让它成为一个类。然而,在PHP中并非总是如此。但它在90%的情况下都能在PHP中工作。(根据我的经验)

可能,这更多的是违反单一责任原则的问题,而这正导致TDD问题

这是一件好事,这意味着TDD暴露了设计缺陷。故事大概是这样的

如果这些方法不是公开的,只是将代码分解成更易于消化的块,老实说,我不会在意


如果这些方法是公共的,那么你就有问题了。遵循规则,“类实例的任何公共方法必须在任何点都可以调用”。也就是说,如果您需要对方法调用进行某种排序,那么是时候将该类拆分了。

方法的效果并不总是直接(或立即)对客户端可见。而且,仅仅因为它给出了正确的结果并不意味着它用了正确的方法。这就是使用模拟的原因——它们可以告诉你是否正确调用了函数或方法。嗯……在我看来,玩一个对象的内部来测试声音是向系统引入更多错误的好方法(Eizenberg眼睛规则)。将一个方法作为黑盒来测试似乎不符合TDD。测试驱动了该方法的设计。mock不仅为您提供了一种适当的方法来隔离测试purpo的方法