PHPUnit TDD问题

时间:2015-01-31 22:38:05

标签: php design-patterns phpunit tdd

我学习TDD并且我有一些问题。 - 我们的想法是创建一个由测试引导的简单工厂。问题是我的测试覆盖率不是100%,这就是我的问题所在。

在我向您展示代码之前,让我解释一下我想要的内容。

  • 工厂负责实例化类并测试它我必须模拟类的实例化,这样做我使用具体的类,它可以工作,但当我看到测试覆盖率时,具体课程出现并且没有100%经过测试。 (你会明白的)。 - 我想避免这种情况,我想尽可能抽象地测试工厂。
  • 另一个问题是我想将工厂插入构造函数的依赖项,如何测试?

报道:

  • PizzaStore.php - 33,33% - 具有工厂依赖性的构造函数未经过测试。
  • PizzaFactory.php - 100%
  • Pizza.php - 100%
  • Pizza / Greek.php - 0.00% - 使用具体类测试工厂后出现。

yoda

PizzaTest.php:

<?php namespace Pattern\SimpleFactory;

use PHPUnit_Framework_TestCase;

class PizzaTest extends PHPUnit_Framework_TestCase {

  private $pizza;

  public function setUp()
  {
    $this->pizza = $this->getMockForAbstractClass('Pattern\SimpleFactory\Pizza');
  }

  /**
   * @covers Pattern\SimpleFactory\Pizza::setName
   * @covers Pattern\SimpleFactory\Pizza::getName
   */
  public function testPizzaSetsAndReturnsTheExpectedName()
  {
    $pizzaName = 'Greek Pizza';
    $this->pizza->setName($pizzaName);
    $this->assertEquals($pizzaName, $this->pizza->getName());
  }

  /**
   * @covers Pattern\SimpleFactory\Pizza::setDescription
   * @covers Pattern\SimpleFactory\Pizza::getDescription
   */
  public function testPizzaSetsAndReturnsTheExpectedDescription()
  {
    $pizzaDescription = 'A Pepperoni-style pizza with dough, tomato, and cheese';
    $this->pizza->setDescription($pizzaDescription);
    $this->assertEquals($pizzaDescription, $this->pizza->getDescription());
  }

}

PizzaStoreTest.php:

<?php namespace Pattern\SimpleFactory;

use PHPUnit_Framework_TestCase;

class PizzaStoreTest extends PHPUnit_Framework_TestCase {

  /**
   * @covers Pattern\SimpleFactory\PizzaStore::order
   */
  public function testStoreShouldReturnsTheRequestedPizzaWhenOrdered()
  {
    $factory = new PizzaFactory();
    $store = new PizzaStore($factory);
    $pizza = $store->order('greek');
    $this->assertInstanceOf('Pattern\SimpleFactory\Pizza', $pizza);
  }

}

PizzaFactoryTest.php:

<?php namespace Pattern\SimpleFactory;

use PHPUnit_Framework_TestCase;

class PizzaFactoryTest extends PHPUnit_Framework_TestCase {

  /**
   * @var PizzaFactory
   */
  private $pizzaFactory;

  public function setUp()
  {
    $this->pizzaFactory = new PizzaFactory();
  }

  /**
   * @covers Pattern\SimpleFactory\PizzaFactory::make
   */
  public function testPizzaFactoryMakesAPizza()
  {
    $pizza = $this->pizzaFactory->make('greek');
    $this->assertInstanceOf('Pattern\SimpleFactory\Pizza', $pizza);
  }

  /**
   * @covers Pattern\SimpleFactory\PizzaFactory::make
   */
  public function testPizzaFactoryReturnsNullWhenMakingANonexistentPizza()
  {
    $pizza = $this->pizzaFactory->make(null);
    $this->isNull($pizza);
  }

}

PizzaStore.php:

<?php namespace Pattern\SimpleFactory;

/**
 * @package Pattern\SimpleFactory
 */
class PizzaStore {

  /**
   * @var PizzaFactory
   */
  private $factory;

  /**
   * @param PizzaFactory $factory
   */
  function __construct(PizzaFactory $factory)
  {
    $this->factory = $factory;
  }

  /**
   * @param string $name
   * @return null|Pizza
   */
  public function order($name)
  {
    return $this->factory->make($name);
  }

}

PizzaFactory.php:

<?php namespace Pattern\SimpleFactory;

/**
 * @package Pattern\SimpleFactory
 */
class PizzaFactory {

  /**
   * @var array
   */
  private $pizzas = [
    'greek'     => 'Pattern\SimpleFactory\Pizza\Greek',
    'pepperoni' => 'Pattern\SimpleFactory\Pizza\Pepperoni',
  ];

  /**
   * @param string $name
   * @return null|Pizza
   */
  public function make($name)
  {
    if (isset($this->pizzas[$name]))
    {
      return new $this->pizzas[$name];
    }

    return null;
  }

}

Pizza.php:

<?php namespace Pattern\SimpleFactory;

/**
 * @package Pattern\SimpleFactory
 */
abstract class Pizza {

  /**
   * @var string
   */
  private $name;
  /**
   * @var string
   */
  private $description;

  /**
   * @return string
   */
  public function getName()
  {
    return $this->name;
  }

  /**
   * @param string $name
   */
  public function setName($name)
  {
    $this->name = $name;
  }

  /**
   * @return string
   */
  public function getDescription()
  {
    return $this->description;
  }

  /**
   * @param string $description
   */
  public function setDescription($description)
  {
    $this->description = $description;
  }

}

披萨\ Pepperoni.php:

<?php namespace Pattern\SimpleFactory\Pizza;

use Pattern\SimpleFactory\Pizza;

/**
 * @package Pattern\SimpleFactory\Pizza
 */
class Pepperoni extends Pizza {

  function __construct()
  {
    parent::setName('Pizza Pepperoni');
    parent::setDescription('A Pepperoni-style pizza with dough, tomato, and cheese');
  }

}

披萨\ Greek.php:

<?php namespace Pattern\SimpleFactory\Pizza;

use Pattern\SimpleFactory\Pizza;

/**
 * @package Pattern\SimpleFactory\Pizza
 */
class Greek extends Pizza {

  function __construct()
  {
    parent::setName('Pizza Greek');
    parent::setDescription('A Greek-style pizza with feta cheese, onion, olive and tomato');
  }

}

就是这样,有什么不对的,请说出来。提前谢谢。

1 个答案:

答案 0 :(得分:4)

  

工厂负责实例化类并对其进行测试   我必须模拟类的实例化,这样做我就是这样   使用具体的类,它的工作原理,但当我看到测试覆盖率,   具体类出现,并没有100%测试。 (你会   了解)。 - 我想避免这种情况,我想测试工厂   尽可能抽象。

PizzaStore课程失踪的原因是因为您没有在工厂找不到课程时测试案例。

缺少测试用例:

/**
 * @covers Pattern\SimpleFactory\PizzaStore::order
 */
public function testStoreShouldReturnsTheRequestedPizzaWhenOrdered()
{
    $factory = new PizzaFactory();
    $store = new PizzaStore($factory);
    $pizza = $store->order('not exists pizza type');
    $this->assertNull($pizza);
}
  

另一个问题是我想将工厂插入   依赖于构造函数,如何测试呢?

您可以使用模拟的PizzaFactory类,然后调用verify。我不知道这如何使用PHPUnit模拟,但在ouzo-goodies模拟实用程序中看起来像:

public function testFactory()
{
    $factory = Mock::create('\Pattern\SimpleFactory\PizzaFactory');
    Mock::when($factory)->make()->thenReturn(new \Pattern\SimpleFactory\Pizza\Greek());

    $magic = new Magic($factory);

    $order = $magic->order();

    Mock::verify($factory)->make();
    $this->assertInstanceOf('\Pattern\SimpleFactory\Pizza\Greek', $order);
}

Mock::verify方法检查调用方法PizzaFactory::make

此外,在我看来,更好地使用interface over inheritance