|
2#
楼主 |
发表于 2006-10-28 22:08:46
|
只看该作者
5.3.5.1 Test Methods
public Tence_Assertion Tence_TestCase::testXXX()
Any public nullary method whose name begins with "test" is considered a test method, and must return an instance of Tence_Assertion. Failing to do so results in the test method being marked as defective.
Important part of robustness in tests is isolation: any test that depends on things it doesn't control is fragile. This library guarantees that each test method will be called on a separate test case instance.
5.3.5.2 setUp() and tearDown()
public void Tence_TestCase::setUp()
public void Tence_TestCase::tearDown()
setUp() and/or tearDown() can be used for setup and cleanup tasks common to all test methods of the test case. setUp() is called before every test method, tearDown() afterwards, both on the same instance as the test method.
If setUp() throws, tearDown() is run immediately. The test method is skipped. If either of these two methods throws, the test method is marked as defective. Exceptions thrown from setUp() mute all exceptions thrown from tearDown().
5.3.5.3 Assertion Methods
Note that none of the assertFoo() methods performs any type conversions on the tested values. This eliminates a few false negatives: for example, 0 == false, but their string representations differ drammaticaly ("0" vs. ""); the sooner this kind of things is caught the better.
assertTrue(1) will fail, as will assertEquals(1, "1")!
Tence_Assertion Tence_TestCase::assertTrue($bool, $msg = '')
Checks that $bool is true.
Tence_Assertion Tence_TestCase::assertFalse($bool, $msg = '')
Checks that $bool is false.
Tence_Assertion Tence_TestCase::assertEquals($exp, $act, $msg = '')
Checks that $act equals $exp.
Instances of the same class are compared by value (using the == operator).
Tence_Assertion Tence_TestCase::assertSame($exp, $act, $msg = '')
Checks that $act equals $exp.
Instances of the same class are compared by identity (using the === operator).
Tence_Assertion Tence_TestCase::assertNull($any, $msg = '')
Checks that $bool is null.
Tence_Assertion Tence_TestCase::assertNotNull($any, $msg = '')
Checks that $bool is not null.
Tence_Assertion Tence_TestCase::assertIsA($o, $class, $msg = '')
Checks that $o is an instance of $class.
Tence_Assertion Tence_TestCase::assertMatches($re, $val, $msg = '')
Checks that $val matches the Perl-compatible regular expression $re.
void Tence_TestCase::setExpectedException($className)
Iff the code following the call of this method throws an exception that is of (or a descendant of) class $className, the test method will "return" successful assertion. All other outcomes cause a failure.
Actual return value of this test method will be ignored.
void Tence_TestCase::run(Tence_Reporter $reporter)
Calls every public method whose name begins with test. Result of each invocation is passed to $reporter.
Tence_RunnableTest[] Tence_TestCase::getTests()
Returns an array of Tence_TestMethod instances, one for each test method.
5.3.6 Tence_TestSuite
Tence_TestSuite & Tence_TestSuite::add(Tence_RunnableTest $test)
Adds $test to the suite. Returns $this.
void Tence_TestSuite::run(Tence_Reporter $reporter)
Calls run($reporter) on every $test it contains.
Tence_RunnableTest[] Tence_TestSuite::getTests()
Returns an array of all $tests added so far.
6 Examples
Here are a few examples to illustrate usage of Tence_TestCase and Tence_TestSuite in writing tests, and tence for their execution.
strcmp_allLowerCaseTest shows one approach to writing test cases: the implementation under test, strcmp() is a pure function so there's only one thing to assert after each strcmp() call.
<?php
class strcmp_allLowerCaseTest extends Tence_TestCase
{
function setUp()
{
$this->oldLocale = setlocale(LC_COLLATE, 'C');
}
function tearDown()
{
setlocale(LC_COLLATE, $this->oldLocale);
}
function testEqualStringsReturnZero()
{
return $this->assertEquals(0, strcmp('aaa', 'aaa'));
}
function testLeftSortsBeforeReturnsMinusOne()
{
return $this->assertEquals(-1, strcmp('aaa', 'aab'));
}
function testRightSortsBeforeReturnsOne()
{
return $this->assertEquals(1, strcmp('aab', 'aaa'));
}
function testShortLeftReturnsMinusOne()
{
return $this->assertEquals(-1, strcmp('aaa', 'aaaa'));
}
function testShortRightReturnsOne()
{
return $this->assertEquals(1, strcmp('aaaa', 'aaa'));
}
}
Paste the above code into mytests.php and run through tence:
% tence mytests.php strcmp_allLowerCaseTest
.....
5 run, 0 failed, 0 threw, 0 malformed
You can (and probably want to) group tests into suites:
class strcmp_mixedCaseTest extends Tence_TestCase
{
// present for demonstration purposes, body is irrelevant...
// contains 5 test methods
}
class AllStrcmpTests extends Tence_TestSuite
{
function __construct()
{
$this
->add(new strcmp_allLowerCaseTest)
->add(new strcmp_mixedCaseTest)
;
}
}
Let's say one of the tests failed (reason for the failure is beside the point of this example):
% tence mytests.php AllStrcmpTests
.....F....
strcmp_mixedCaseTest::testUpperLeftReturnsMinusOne
expected integer(-1) got integer(1)
10 run, 1 failed, 0 threw, 0 malformed
You might have more than one thing to assert after an operation. For example, an instance of a MyFileReader class has to return consistent values from its contents() and lines() methods:
interface FileReader
{
/* string[] */ function contents();
/* int */ function lines();
}
class MyFileReader implements FileReader
{
function __construct($path)
{
// ...
}
// ...
}
Such code is better tested with the tested operation performed in setUp() and the various consequences tested in individual test methods:
class EmptyFileReaderTest extends Tence_TestCase
{
private $reader;
function setUp()
{
$this->file = tempnam('/tmp', 'whatever');
$this->reader = new MyFileReader($this->file);
}
function tearDown()
{
unlink($this->file);
}
function testContentsReturnsEmptyArray()
{
return $this->assertEquals(array(), $this->reader->contents());
}
function testLinesReturnsZero()
{
return $this->assertEquals(0, $this->reader->lines());
}
}
EmptyFileReaderTest alone is insufficient since it leaves the MyFileReader constructor (partially) untested: what if the path passed to the constructor names a non-file type of object or doesn't exist at all? What happens if the permissions disallow read access? These are different tests, and need to be written as well:
class MyFileReaderConstructionErrors
{
function testNonexistentFile()
{
$tmpdir = $this->mkdtemp();
$file = sprintf('%s/nonexistent', $tmpdir->path());
$this->setExpectedException('FileDoesNotExist');
new MyFileReader($file);
}
function testInsufficientPermissions()
{
$tmpdir = $this->mkdtemp();
$file = sprintf('%s/nonexistent', $tmpdir->path());
touch($file);
chmod($file, 0);
$this->setExpectedException('InsufficientPermissions');
new MyFileReader($file);
}
}
The above code relies on the fact that Tence_TestCase::mkdtemp() returns an object whose destructor removes the directory recursively.
It might make sense to compose the MyFileReader tests into a suite:
class FileReaderTests extends Tence_TestSuite
{
function __construct()
{
$this
->add(new MyFileReaderConstructionErrors)
->add(new EmptyFileReaderTest)
;
}
}
Tence_TestSuite allows arbitrary nesting:
class AllMyTests extends Tence_TestSuite
{
function __construct()
{
$this
->add(new AllStrcmpTests)
->add(new FileReaderTests)
;
}
}
Presuming the failing test in strcmp_mixedCaseTest has been fixed:
% tence mytests.php AllMyTests
............
14 run, 0 failed, 0 threw, 0 malformed
Note that suites are not required, you can name any number of classes implementing Tence_RunnableTest on the command line and tence will construct an anonymous suite on the fly:
% tence mytests.php strcmp_allLowerCaseTest strcmp_mixedCaseTest \
> MyFileReaderConstructionErrors EmptyFileReaderTest
............
14 run, 0 failed, 0 threw, 0 malformed
7 Releases
7.1 Release types
Testilence releases fall into one of the following four categories:
snapshots
bugfix releases
new functionality
backward-incompatible change
7.2 Release versioning
Each release is tagged with a version number string with the following structure (ABNF [14]): |
|