PHPUnit: как сохранить аргументы, переданные в метод-заглушку - PullRequest
4 голосов
/ 21 марта 2011

Метод setValue () В классе SomeClass принимает аргументы и не может вернуть эти аргументы для утверждения make.

Могу ли я создать заглушку для метода setValue (), которая позволяет сохранять аргументы, переданные этому методу?

<code>
class SomeClass {
  public function setValue($name, $value)
  {
    // do some stuff
  }
  public function doSomething(array $values)
  {
    foreach ($values as $name=>$value) {
      $this->setValue($name, trim($value));
    }
  }
}</p>

<p>class TestSomeClass extends PHPUnit_Framework_TestCase {<br>
  public function testDoSomething()
  {
    $mock = $this->getMock('SomeClass', array('setValue'));
    $mock->doSomething(array('v1'=>'  string  '));
    // here need I need assert like this
    $this->assertEquals('string', $argumentPassedToSetValue);
  }
}

Ответы [ 4 ]

3 голосов
/ 21 марта 2011

О том, как проверить работоспособность настройки, см. Ответ @Gordons.

Я бы хотел сказать, что вам не нужно , чтобы проверить это.

Ваши модульные тесты должны убедиться, что public API вашего класса работает как положено. Вам не важно (для тестирования), как ваши значения хранятся внутри, поэтому вам не нужно требовать , чтобы утверждать это. При таком способе ваши тесты также проверяют только , что делает ваш класс , а не , как класс делает это

Дело в том, что вам не нужно менять свои тесты, когда вы меняете свой класс, не влияя на то, что он делает

Ради аргумента, скажем, SomeClass - это то, что в конце концов выплевывает HTML.

class SomeClass {
  public function setValue($name, $value)
  {
    // do some stuff
  }

  public function doSomething(array $values)
  {
    foreach ($values as $name=>$value) {
      $this->setValue($name, trim($value));
    }
  }

  public function createHTML() 
  {
    $return = "";
    foreach($this->values as $key => $value) { 
         $return .= "<div is='$key'>$value</div>"; 
    }
    return $return;
  }

}

Если это все, что делает ваш класс, то абсолютно достаточный тест для этого класса может выглядеть следующим образом:

class SomeClassTest extends PHPUnit_Framework_TestCase {

    public function testHtmlGenerationWithTwoValuesSet() {
        $o = new SomeClass();
        $o->setValue("foo", "bar");
        $o->setValue("x", "y");
        $result = $o->createHTML();
        $this->assertSame(
             2,
             substr_count("<div>", $result),
             "There should be as many divs as there are values set"
        );
        $this->assertTrue(
             strpos("<div>bar</div>") !== false
             "String should contain a set value enclosed in divs"
        );
    }

}

Опять же: речь идет о тестировании поведения вашего класса, не о тестировании каждого метода самостоятельно . Ваш набор тестов будет гораздо более ценным, если вы справитесь с этим

Хотя пример с html может быть неправильным, он показывает, как довольно хорошо тестировать поведение (надеюсь)

2 голосов
/ 21 марта 2011

Нет. Моксы являются заменами для Зависимостей , а не для фактического TestSubject.

Если ваш метод setValue устанавливает непубличные свойства, вы можете использовать assertAttributeEquals:

class SomeClass
{
    protected $foo;
    public function setFoo($val)
    {
        $this->foo = trim($val);
    }
}

class SomeClassTest extends PHPUnit_Framework_TestCase
{
    public function testSetFooTrimsArgument()
    {
        $testSubject = new SomeClass;
        $testSubject->setFoo('  bar  ');
        $this->assertAttributeEquals(
            'bar',  /* expected value */
            'foo',  /* attribute name */
            $testSubject
        );
    }
}

Более подробную информацию о том, как проверить рядовых, можно найти в

1 голос
/ 21 марта 2011

В этом простом случае вы можете указать аргумент, который вы ожидаете передать setValue().

class TestSomeClass extends PHPUnit_Framework_TestCase
{
  public function testDoSomething()
  {
    $mock = $this->getMock('SomeClass', array('setValue'));
    $mock->expects($this->once())->method('setValue')->with('v1', 'string');
    $mock->doSomething(array('v1'=>'  string  '));
  }
}

Я согласен с edorian, однако, что вам лучше тестировать то, что выходит из класса через публичный API. Непосредственное тестирование внутренней реализации означает обновление большего количества тестов при каждом изменении или рефакторинге.

1 голос
/ 21 марта 2011

Вы можете использовать returnCallback для выполнения произвольного кода, такого как вспомогательный метод тестирования вашего тестового примера.Например, если setValueTester является методом в вашем тестовом примере, это ожидание перенаправит вызов setValue на макет к нему.Аргументы доступны с func_get_args.

$mock->expects($this->any())->method("setValue")->will($this->returnCallback(array($this,"setValueTester"));
...