第三章PHPUnit的目的
到此为止,我们只有两个对Array和内建函数sizeof()的测试。当我们开始测试大量的array_*()函数时,每个都需要一个测试。我们可以每个都从头写起。但是,更好的方法是一次性写好一个测试基础构架,以后就只用写每个测试不同的部分。PHPUnit就是这样一个基础构架。
例5展示了如何用PHPUnit重写例4中的两个测试。
例5. 用PHPUnit测试 Array和sizeof().
<?php
require_once 'PHPUnit2/Framework/TestCase.php';
class ArrayTest extends PHPUnit2_Framework_TestCase {
public function testNewArrayIsEmpty( ) {
// 创建数组fixture。
$fixture = Array( );
// 断言数组fixture的大小是0。
$this->assertEquals(0, sizeof($fixture));
}
public function testArrayContainsAnElement( ) {
// 创建数组fixture。
$fixture = Array( );
// 为数组fixture增加一个成员。
$fixture[] = 'Element';
//断言数组fixture的大小是1。
$this->assertEquals(1, sizeof($fixture));
}
}
?>
例5告诉我们用PHPUnit来写测试的基本步骤是:
1. 类Class的测试类是ClassTest。
2. ClassTest一般继承PHPUnit2_ Framework_TestCase。
3. 测试是公有方法,没有参数,名字是test*。
4. 在测试方法中,断言函数,如assertEquals()(见表6)用于断言实际值是否匹配期望值。
一个如PHPUnit的框架需要解决一系列问题,有些看起来互相冲突。测试必须同时满足以下条件:
易学
测试必须容易学,否则,开发人员不会去学
易开发
测试必须容易开发,否则,开发人员不会去开发
易读
测试代码必须没有外部关系,这样测试本身不会在杂乱无章中迷失。
容易执行
测试应该很容易执行,执行的结果以一种清楚和明确的格式表达出来。
快速执行
测试应该执行的很快,这样每天才能执行上千次。
代码隔离
测试之间不能互相影响,测试顺序的改变不应该影响结果。
可组合的
我们应该可以以任何组合来运行测试,这是代码隔离的一个必然结果。
这些约束条件有两个主要的冲突
易学vs易开发
测试通常不需要应用到编程全部的灵活性。很多测试工具提供了它们自己的测试脚本语言,这些语言只有书写测试所需特性的最小集,因为没有噪声来干扰你的测试内容,写出来的测试易读易写。但是学一种新的编织邮件和一套工具还是不方便的,容易混淆视听。
代码隔离vs快速执行
如果你想要一个测试的结果不影响另一个,每个测试在开始运行的阶段,都需要创建测试的全专题,返回后又要恢复运行之前的状态。可是,设置状态需要的时间很长(如,连接到数据库,用真实数据初始化到一个已知状态)
PHPUnit解决这个问题的办法是采用PHP作为测试语言。有时,全功能的PHP对于书写短小的,直接的测试是过于强大了,不过,我们利用的程序员已经有使用PHP的全部经验。因为我们需要说服勉强的测试人员,降低书写这些初始测试的门槛是及其重要的。
--------------------------------------------------------------------------------------------------------------------
原文:
Chapter 3. PHPUnit's Goals
So far, we only have two tests for the Array built-in and the sizeof( ) function. When we start to test the numerous array_*( ) functions PHP offers, we will need to write a test for each of them. We could write all these tests from scratch. However, it is much better to write a testing infrastructure once and then write only the unique parts of each test. PHPUnit is such an infrastructure.
Example 5 shows how we have to rewrite our two tests from Example 4 so that we can use them with PHPUnit.
Example 5. Testing Array and sizeof( ) with PHPUnit
<?php
require_once 'PHPUnit2/Framework/TestCase.php';
class ArrayTest extends PHPUnit2_Framework_TestCase {
public function testNewArrayIsEmpty( ) {
// Create the Array fixture.
$fixture = Array( );
// Assert that the size of the Array fixture is 0.
$this->assertEquals(0, sizeof($fixture));
}
public function testArrayContainsAnElement( ) {
// Create the Array fixture.
$fixture = Array( );
// Add an element to the Array fixture.
$fixture[] = 'Element';
// Assert that the size of the Array fixture is 1.
$this->assertEquals(1, sizeof($fixture));
}
}
?>
Example 5 shows the basic steps for writing tests with PHPUnit:
The tests for a class Class go into a class ClassTest.
ClassTest inherits (most of the time) from PHPUnit2_ Framework_TestCase.
The tests are public methods that expect no parameters and are named test*.
Inside the test methods, assertion methods such as assertEquals( ) (see Table 6) are used to assert that an actual value matches an expected value.
A framework such as PHPUnit has to resolve a set of constraints, some of which seem to conflict with each other. Simultaneously, tests should be:
Easy to learn to write.
Tests should be easy to learn to write; otherwise, developers will not learn to write them.
Easy to write.
Tests should be easy to write; otherwise, developers will not write them.
Easy to read.
Test code should contain no extraneous overhead so that the test itself does not get lost in the noise that surrounds it.
Easy to execute.
Tests should run at the touch of a button and present their results in a clear and unambiguous format.
Quick to execute.
Tests should run fast so they can be run hundreds or thousands of times a day.
Isolated.
Tests should not affect each other. If the order in which the tests are run changes, the results of the tests should not change.
Composable.
We should be able to run any number or combination of tests together. This is a corollary of isolation.
There are two main clashes within this group of constraints:
Easy to learn to write versus easy to write.
Tests do not generally require all the flexibility of a programming language. Many testing tools provide their own scripting language that includes only the minimum necessary features for writing tests. The resulting tests are easy to read and write because they have no noise to distract you from the content of the tests. However, learning yet another programming language and set of programming tools is inconvenient and clutters the mind.
Isolated versus quick to execute.
If you want the results of one test not to affect the results of another test, each test should create the full state of the testing before it begins to execute, and return the world to its original state when it finishes. However, setting it up can take a long time (e.g., connecting to a database and initializing it to a known state using realistic data).
PHPUnit attempts to resolve these conflicts by using PHP as the testing language. Sometimes the full power of PHP is overkill for writing short, straight-line tests, but by using PHP, we leverage all the experience and tools programmers already have in place. Because we are trying to convince reluctant testers, lowering the barrier to writing those initial tests is particularly important.
PHPUnit errs on the side of isolation over quick execution. Isolated tests are valuable because they provide high-quality feedback. You do not get a report with a bunch of test failures that were really caused because one test at the beginning of the suite failed and left the world messed up for the rest of the tests. This orientation toward isolated tests encourages designs with a large number of simple objects. Each object can be tested quickly in isolation. The result is better designs and faster tests.
PHPUnit assumes that most tests succeed, and it is not worth reporting the details of successful tests. When a test fails, that fact is worth noting and reporting. The vast majority of tests should succeed and are not worth commenting on, except to count the number of tests that run. This is an assumption that is really built into the reporting classes and not into the core of PHPUnit. When the results of a test run are reported, you see how many tests were executed, but you only see details for those that failed.
Tests are expected to be fine-grained, testing one aspect of one object. Hence, the first time a test fails, execution of the test halts, and PHPUnit reports the failure. It is an art to test by running many small tests. Fine-grained tests improve the overall design of the system.
When you test an object with PHPUnit, you do so only through the object's public interface. Testing based only on publicly visible behavior encourages you to confront and solve difficult design problems before the results of poor design can affect large parts of the system.