English 中文(简体)
重构方法已经在同一个对象为更可测试的依赖关系(PHP)
原标题:
  • 时间:2009-04-08 02:07:03
  •  标签:

我现在有一个方法在我的类,调用其他方法,一些从其他相同的对象和其他对象。


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的新游戏,但是一些我认为< em > < / em >我理解关键前提有更多的可测试的代码组成,松散耦合,一些依赖项注入/反转控制的策略。

我该如何去重构一个方法更可测试的在这个特殊的情况?

我美元通过<代码> < /代码>对象引用作为参数的方法,这样我可以很容易地模拟/存根合作方法吗?这是推荐的还是玩的太过头了吗?


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

        # ...

        return $var5;
    }
}

同时,很明显,我依赖性与TDD是非常大的问题,必须考虑如何注入一个存根/模拟的方法测试。大多数TDDers使用DI /国际奥委会作为主要战略公众依赖?此时变得夸张吗?你能给一些指针有效地这样做吗?

最佳回答

这些都是一些好问题……首先我想说,我真的不知道JS,但我是一个单元测试人员和处理这些问题。我第一次想指出,JsUnit存在如果你不使用它。

我不会过于担心你的方法调用其他方法在同一类…这是注定要发生的。让我更担心的是其他对象的创建,这取决于它是复杂的。

例如,如果你是实例化一个类,并在网络上各种各样的操作,太重了,一个简单的单元测试。你更愿意做的是模拟依赖类,这样你就可以有对象产生结果你会收到网络上的业务,不引起的开销网络:网络故障,时间等……

通过在其他对象中有点乱。人们通常做的是有工厂方法实例化另一个对象。工厂方法可以决定,基于你是否测试(通常是通过一个标记)是否要实例化的对象或模拟。事实上,您可能想要让其他成员对象类,并在构造函数,调用工厂,或者做决定是否要实例化模拟或真实的东西。在setup函数或在你的测试用例可以设置特殊条件在模拟对象,它将返回正确的值。

同时,要确保你的其他功能测试在同一个班……我希望这可以帮助!

问题回答

像这类的想法并不完全正确。在TDD正在测试类,而不是方法。如果一个方法都有它自己的责任,并提供它年代的(单独测试的)功能应该搬到一个单独的类。否则它就打破了整个OOP封装的事情。特别是它打破单一责任原则。

在你的情况下,我会测试方法提取到另一个类,注入<代码>美元var1 > < /代码,<代码>美元var2 < /代码>,<代码>美元var3 < /代码>和<代码>其他> < /代码依赖美元。<代码>其他> < /代码应该嘲笑美元,任何对象这取决于测试类。

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中虽然并不总是如此。但它在PHP在90%的情况下工作。(根据我的经验)

我可能是错的,但我认为应该黑匣子对象/类客户,所以他们的测试客户端(封装我认为是我在寻找这个词)。

有几件事你能做什么:

最好的办法是模拟的,这年代这样一个库:< a href = " http://code.google.com/p/php-mock-function " rel = " nofollow noreferrer " > http://code.google.com/p/php-mock-function < / >

应该让你模拟只有你想要的特定功能。

如果不工作,其次是提供实施<代码> method2 > < /代码作为一个对象的方法在<代码> MyClass类> < /代码。我发现这一个简单的方法,如果你不能直接模拟方法:

class MyClass {
  function __construct($method2Impl) {
    $this->method2Impl = $method2Impl;
  }
  function method2() {
    return $this->method2Imple->call();
  }
}

另一个选择是添加一个“测试”标记,方法行为不同。我不推荐这种——最终你会有不同的代码路径,用他们自己的错误。

另一个选择是子类覆盖你需要的行为。我-真的,不建议这因为你会最终定制覆盖模拟,它本身有缺陷:)。

最后,如果你需要模拟出一个方法,因为它太复杂,< em > < / em >可以是一个好的迹象将它移动到自己的对象和使用的成分(本质上使用<代码> method2Impl > < /代码技术我上面提到的)。

可能,这是更多的单一责任原则被违反,这令投资人TDD的问题。

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

如果这些方法都不公开,只是你你代码分解成更digestable块,老实说,我不关心。

如果这些方法是公开的,那么你有一个问题。遵循规则,任何公共方法类的实例必须是可调用的任何时候。也就是说,如果你需要某种排序的方法调用,那么它年代时间分解类。





相关问题
热门标签