Browse files

MINOR Improved testing docs

  • Loading branch information...
1 parent 460148c commit 9898cd9a954e702221d32d6aaf7e725ee12c7f6b @chillu chillu committed Jun 2, 2012
@@ -5,20 +5,12 @@ provides us the basics of creating unit tests.
- /**
- * Tests for SiteTree
- */
class SiteTreeTest extends SapphireTest {
- /**
- * Define the fixture file to use for this test class
- */
+ // Define the fixture file to use for this test class
static $fixture_file = 'SiteTreeTest.yml';
- /**
+ /**
* Test generation of the URLSegment values.
* - Turns things into lowercase-hyphen-format
* - Generates from Title by default, unless URLSegment is explicitly set
@@ -49,32 +41,26 @@ There are a number of points to note in this code fragment:
* Your test is a **subclass of SapphireTest**. Both unit tests and functional tests are a subclass of `[api:SapphireTest]`.
* **static $fixture_file** is defined. The testing framework will automatically set up a new database for **each** of
-your tests. The initial database content will be sourced from the YML file that you list in $fixture_file. You must
-define this value. Note also that, for the time being, you can only point to one YML file for each test class.
+your tests. The initial database content will be sourced from the YML file that you list in $fixture_file. The property can take an array of fixture paths.
* Each **method that starts with the word "test"** will be executed by the TestRunner. Define as many as you like; the
database will be rebuilt for each of these.
* **$this->objFromFixture($className, $identifier)** can be used to select one of the objects named in your fixture
file. To identify to the object, we provide a class name and an identifier. The identifier is specified in the YML
file but not saved in the database anywhere. objFromFixture() looks the `[api:DataObject]` up in memory rather than using the
database. This means that you can use it to test the functions responsible for looking up content in the database.
-* **$this->assertEquals()** is one of the many assert... functions that PHPUnit provides us. See below for more
## Assertion commands
-**$this->assertEquals()** is an example of an assertion function. These functions form the basis of our tests - a test
+**$this->assertEquals()** is an example of an assertion function.
+These functions form the basis of our tests - a test
fails if and only if one or more of the assertions fail.
-There are many assertions available:
-* See [the PHPUnit manual chapter 22](
+See [the PHPUnit manual](
for a listing of all PHPUnit's built-in assertions.
-* **$this->assertEmailSent($to, $from, $subject, $content)**: When an email is "sent" during a test run, it's not
-actually sent. Instead, it is logged in an internal register. You can use assertEmailSent() to verify that an email
-was sent. Each of the arguments can be a string, for an exact match, or, a preg_match() compatible regular expression,
-if it starts with "/".
+The `[api:SapphireTest]` class comes with additional assertions which are more
+specific to the framework, e.g. `[assertEmailSent](api:SapphireTest->assertEmailSent())`
+which can simulate sending emails through the `Email->send()` API without actually
+using a mail server (see the [testing emails](email-sending)) guide.
## The Database YAML file
@@ -8,8 +8,6 @@ The SilverStripe core contains various features designed to simplify the process
* [Troubleshooting](testing-guide-troubleshooting): Frequently asked questions list for testing issues
* [Why Unit Test?](why-test): Why should you test and how to start testing
-## Introduction
If you are familiar with PHP coding but new to unit testing, you should read the [Introduction](/topics/testing) and
check out Mark's presentation [Getting to Grips with SilverStripe Testing](
@@ -99,7 +97,6 @@ Some people may note that we have used the same naming convention as Ruby on Rai
Tutorials and recipes for creating tests using the SilverStripe framework:
* **[Create a SilverStripe Test](/topics/testing/create-silverstripe-test)**
-* **Load Test Fixtures**
* **[Create a Functional Test](/topics/testing/create-functional-test)**
* **[Test Outgoing Email Sending](/topics/testing/email-sending)**
@@ -145,9 +142,4 @@ understand the problem space and discover suitable APIs for performing specific
**Behavior Driven Development (BDD):** An extension of the test-driven programming style, where tests are used primarily
for describing the specification of how code should perform. In practice, there's little or no technical difference - it
all comes down to language. In BDD, the usual terminology is changed to reflect this change of focus, so *Specification*
-is used in place of *Test Case*, and *should* is used in place of *expect* and *assert*.
-## Feedback
-If you have a topic you would like covered in these section please ask for it on our [Bug Tracker](
+is used in place of *Test Case*, and *should* is used in place of *expect* and *assert*.
@@ -15,3 +15,47 @@ It can be fixed by running the following commands:
pear install -f phpunit/DbUnit
pear install -f phpunit/PHPUnit_MockObject
pear install -f phpunit/PHPUnit_Selenium
+## My tests fail seemingly random when comparing database IDs
+When defining fixtures in the YML format, you only assign aliases
+for them, not direct database IDs. Even if you insert only one record
+on a clean database, it is not guaranteed to produce ID=1 on every run.
+So to make your tests more robust, use the aliases rather than hardcoded IDs.
+Also, some databases don't return records in a consistent sort order
+unless you explicitly tell them to. If you don't want to test sort order
+but rather just the returned collection,
+ :::php
+ $myPage = $this->objFromFixture('Page', 'mypage');
+ $myOtherPage = $this->objFromFixture('Page', 'myotherpage');
+ $pages = DataObject::get('Page');
+ // Bad: Assumptions about IDs and their order
+ $this->assertEquals(array(1,2), $pages->column('ID'));
+ // Good: Uses actually created IDs, independent of their order
+ $this->assertContains($myPage->ID, $pages->column('ID'));
+ $this->assertContains($myOtherPage->ID, $pages->column('ID'));
+## My fixtures are getting complicated, how do I inspect their database state?
+Fixtures are great because they're easy to define through YML,
+but sometimes can be a bit of a blackbox when it comes to the actual
+database state they create. These are temporary databases, which are
+destructed directly after the test run - which is intentional,
+but not very helpful if you want to verify that your fixtures have been created correctly.
+SilverStripe comes with a URL action called `dev/tests/startsession`.
+When called through a web browser, it prompts for a fixture file
+which it creates a new database for, and sets it as the current database
+in this browser session until you call `dev/tests/endsession`.
+For more advanced users, you can also have a look in the `[api:YamlFixture]`
+class to see what's going on behind the scenes.
+## My database server is cluttered with `tmpdb...` databases
+This is a common problem due to aborted test runs,
+which don't clean up after themselves correctly
+(mostly because of a fatal PHP error in the tests).
+The easiest way to get rid of them is a call to `dev/tests/cleanupdb`.

0 comments on commit 9898cd9

Please sign in to comment.