如果我正在编写接口,我通常会指定实现应该公开的行为。为了确保这种行为,应该针对该接口编写测试。如何以一种方式最好地编写和组织这些测试,实现的编写者可以轻松地使用它们来确保它们的实现满足要求? 是否有一些扩展(编写子类)接口测试的方法或者我是否实现了像工厂一样的设计模式?
答案 0 :(得分:2)
我明白为什么人们告诉你你不应该测试接口。 但那不是你要问的。 您要求为实现特定接口的多个类执行相同的单元测试的更简单方法。 我使用@dataProvider注释(PHPUnit,是)。
我们假设你有这些课程:
interface Shape {
public function getNumberOfSides();
}
class Triangle implements Shape {
private $sides = 3;
public function getNumberOfSides() {
return $this->sides;
}
}
class Square implements Shape {
private $sides = 4;
public function getNumberOfSides() {
return $this->sides;
}
}
现在,您要测试是否定义了Triangle
getNumberOfSides
的实例并返回3,而对于Square
,它返回4。
这是您使用@dataProvider
编写此测试的方式:
class ShapeTest extends PHPUnit_Framework_TestCase {
/**
* @dataProvider testNumberOfSidesDataProvider
*/
public function testNumberOfSides(Shape $shape, $expectedSides){
$this->assertEquals($shape->getNumberOfSides(), $expectedSides);
}
public function testNumberOfSidesDataProvider() {
return array(
array(new Square(), 5),
array(new Triangle(), 3)
);
}
}
在这里运行phpunit会产生预期的输出:
There was 1 failure:
1) ShapeTest::testNumberOfSides with data set #0 (Square Object (...), 5)
Failed asserting that 5 matches expected 4.
/tests/ShapeTest.php:12
FAILURES!
Tests: 3, Assertions: 2, Failures: 1.
<强>更新强> 失败是因为,正方形的边数是4(按照平方类),并且测试套装试图将结果置为等于5,这是错误的。
答案 1 :(得分:1)
您可以创建一个抽象类,其中包含一些接口契约的基本测试。
请考虑以下示例:
interface FactorialComputer {
public function compute($input);
}
class RecursiveFactorialComputer implements FactorialComputer {
public function compute($input) {
if ($input < 0) {
throw new InvalidArgumentException(...);
}
if ($input == 0 || $input == 1) {
return 1;
}
return $input * $this->compute($input - 1);
}
}
class IterativeFactorialComputer implements FactorialComputer {
public function compute($input) {
$result = 1;
for ($i = 1; $i <= $input; $i++) {
$result *= $i;
}
return $result;
}
}
并测试两种实现:
abstract class AbstractFactorialComputerTest extends PHPUnit_Framework_TestCase {
/**
* @var FactorialComputer
*/
protected $instance;
protected abstract function getComputerInstance();
public function setUp() {
$this->instance = $this->getComputerInstance;
}
/**
* @expectedException InvalidArgumentException
*/
public function testExceptionOnInvalidArgument() {
$this->instance->compute(-1);
}
public function testEdgeCases()
{
$this->assertEquals(1, $this->instance->compute(0));
$this->assertEquals(1, $this->instance->compute(1));
}
...
}
class RecursiveFactorialComputerTest extends AbstractFactorialComputerTest
{
protected abstract function getComputerInstance() {
return new RecursiveFactorialComputer();
}
public function testComputeMethodCallsCount() {
// get mock and test number of compute() calls
}
}
class IterativeFactorialComputerTest extends AbstractFactorialComputerTest
{
protected abstract function getComputerInstance() {
return new IterativeFactorialComputer();
}
}
使用这种方法,每个程序员都应该能够为接口实现创建一个完整的单元测试。