Change order of fixtures in example

* Make the document explicit about the definition of objects before they are referenced.
This commit is contained in:
Rob Ingram 2016-08-23 10:42:14 +12:00
parent 6d4625f067
commit a0d3ad1875

View File

@ -3,20 +3,20 @@ summary: Populate test databases with fake seed data.
# Fixtures
To test functionality correctly, we must use consistent data. If we are testing our code with the same data each
time, we can trust our tests to yield reliable results and to identify when the logic changes. Each test run in
To test functionality correctly, we must use consistent data. If we are testing our code with the same data each
time, we can trust our tests to yield reliable results and to identify when the logic changes. Each test run in
SilverStripe starts with a fresh database containing no records. `Fixtures` provide a way to describe the initial data
to load into the database. The [api:SapphireTest] class takes care of populating a test database with data from
to load into the database. The [api:SapphireTest] class takes care of populating a test database with data from
fixtures - all we have to do is define them.
Fixtures are defined in `YAML`. `YAML` is a markup language which is deliberately simple and easy to read, so it is
Fixtures are defined in `YAML`. `YAML` is a markup language which is deliberately simple and easy to read, so it is
ideal for fixture generation. Say we have the following two DataObjects:
:::php
<?php
class Player extends DataObject {
private static $db = array (
'Name' => 'Varchar(255)'
);
@ -43,6 +43,13 @@ We can represent multiple instances of them in `YAML` as follows:
**mysite/tests/fixtures.yml**
:::yml
Team:
hurricanes:
Name: The Hurricanes
Origin: Wellington
crusaders:
Name: The Crusaders
Origin: Canterbury
Player:
john:
Name: John
@ -53,18 +60,11 @@ We can represent multiple instances of them in `YAML` as follows:
jack:
Name: Jack
Team: =>Team.crusaders
Team:
hurricanes:
Name: The Hurricanes
Origin: Wellington
crusaders:
Name: The Crusaders
Origin: Canterbury
This `YAML` is broken up into three levels, signified by the indentation of each line. In the first level of
This `YAML` is broken up into three levels, signified by the indentation of each line. In the first level of
indentation, `Player` and `Team`, represent the class names of the objects we want to be created.
The second level, `john`/`joe`/`jack` & `hurricanes`/`crusaders`, are **identifiers**. Each identifier you specify
The second level, `john`/`joe`/`jack` & `hurricanes`/`crusaders`, are **identifiers**. Each identifier you specify
represents a new object and can be referenced in the PHP using `objFromFixture`
:::php
@ -72,10 +72,10 @@ represents a new object and can be referenced in the PHP using `objFromFixture`
The third and final level represents each individual object's fields.
A field can either be provided with raw data (such as the names for our Players), or we can define a relationship, as
A field can either be provided with raw data (such as the names for our Players), or we can define a relationship, as
seen by the fields prefixed with `=>`.
Each one of our Players has a relationship to a Team, this is shown with the `Team` field for each `Player` being set
Each one of our Players has a relationship to a Team, this is shown with the `Team` field for each `Player` being set
to `=>Team.` followed by a team name.
<div class="info" markdown="1">
@ -84,10 +84,14 @@ sets the `has_one` relationship for John with with the `Team` object `hurricanes
</div>
<div class="hint" markdown='1'>
Note that we use the name of the relationship (Team), and not the name of the
Note that we use the name of the relationship (Team), and not the name of the
database field (TeamID).
</div>
<div class="hint" markdown='1'>
Also be aware the target of a relationship must be defined before it is referenced, for example the `hurricanes` team must appear in the fixture file before the line `Team: =>Team.hurricanes`.
</div>
This style of relationship declaration can be used for any type of relationship (i.e `has_one`, `has_many`, `many_many`).
We can also declare the relationships conversely. Another way we could write the previous example is:
@ -110,8 +114,8 @@ We can also declare the relationships conversely. Another way we could write the
Origin: Canterbury
Players: =>Player.joe,=>Player.jack
The database is populated by instantiating `DataObject` objects and setting the fields declared in the `YAML`, then
calling `write()` on those objects. Take for instance the `hurricances` record in the `YAML`. It is equivalent to
The database is populated by instantiating `DataObject` objects and setting the fields declared in the `YAML`, then
calling `write()` on those objects. Take for instance the `hurricances` record in the `YAML`. It is equivalent to
writing:
:::php
@ -125,18 +129,18 @@ writing:
$team->Players()->add($john);
<div class="notice" markdown="1">
As the YAML fixtures will call `write`, any `onBeforeWrite()` or default value logic will be executed as part of the
As the YAML fixtures will call `write`, any `onBeforeWrite()` or default value logic will be executed as part of the
test.
</div>
### Defining many_many_extraFields
`many_many` relations can have additional database fields attached to the relationship. For example we may want to
`many_many` relations can have additional database fields attached to the relationship. For example we may want to
declare the role each player has in the team.
:::php
class Player extends DataObject {
private static $db = array (
'Name' => 'Varchar(255)'
);
@ -160,7 +164,7 @@ declare the role each player has in the team.
"Players" => array(
"Role" => "Varchar"
)
);
);
}
To provide the value for the `many_many_extraField` use the YAML list syntax.
@ -176,13 +180,13 @@ To provide the value for the `many_many_extraField` use the YAML list syntax.
Team:
hurricanes:
Name: The Hurricanes
Players:
Players:
- =>Player.john:
Role: Captain
crusaders:
Name: The Crusaders
Players:
Players:
- =>Player.joe:
Role: Captain
- =>Player.jack:
@ -190,20 +194,20 @@ To provide the value for the `many_many_extraField` use the YAML list syntax.
## Fixture Factories
While manually defined fixtures provide full flexibility, they offer very little in terms of structure and convention.
While manually defined fixtures provide full flexibility, they offer very little in terms of structure and convention.
Alternatively, you can use the [api:FixtureFactory] class, which allows you to set default values, callbacks on object
Alternatively, you can use the [api:FixtureFactory] class, which allows you to set default values, callbacks on object
creation, and dynamic/lazy value setting.
<div class="hint" markdown='1'>
SapphireTest uses FixtureFactory under the hood when it is provided with YAML based fixtures.
</div>
The idea is that rather than instantiating objects directly, we'll have a factory class for them. This factory can have
*blueprints* defined on it, which tells the factory how to instantiate an object of a specific type. Blueprints need a
The idea is that rather than instantiating objects directly, we'll have a factory class for them. This factory can have
*blueprints* defined on it, which tells the factory how to instantiate an object of a specific type. Blueprints need a
name, which is usually set to the class it creates such as `Member` or `Page`.
Blueprints are auto-created for all available DataObject subclasses, you only need to instantiate a factory to start
Blueprints are auto-created for all available DataObject subclasses, you only need to instantiate a factory to start
using them.
:::php
@ -219,7 +223,7 @@ In order to create an object with certain properties, just add a third argument:
));
<div class="warning" markdown="1">
It is important to remember that fixtures are referenced by arbitrary identifiers ('hurricanes'). These are internally
It is important to remember that fixtures are referenced by arbitrary identifiers ('hurricanes'). These are internally
mapped to their database identifiers.
</div>
@ -241,7 +245,7 @@ name, we can set the default to be `Unknown Team`.
### Dependent Properties
Values can be set on demand through anonymous functions, which can either generate random defaults, or create composite
Values can be set on demand through anonymous functions, which can either generate random defaults, or create composite
values based on other fixture data.
:::php
@ -258,7 +262,7 @@ values based on other fixture data.
### Relations
Model relations can be expressed through the same notation as in the YAML fixture format described earlier, through the
Model relations can be expressed through the same notation as in the YAML fixture format described earlier, through the
`=>` prefix on data values.
:::php
@ -268,7 +272,7 @@ Model relations can be expressed through the same notation as in the YAML fixtur
#### Callbacks
Sometimes new model instances need to be modified in ways which can't be expressed in their properties, for example to
Sometimes new model instances need to be modified in ways which can't be expressed in their properties, for example to
publish a page, which requires a method call.
:::php
@ -287,8 +291,8 @@ Available callbacks:
### Multiple Blueprints
Data of the same type can have variations, for example forum members vs. CMS admins could both inherit from the `Member`
class, but have completely different properties. This is where named blueprints come in. By default, blueprint names
Data of the same type can have variations, for example forum members vs. CMS admins could both inherit from the `Member`
class, but have completely different properties. This is where named blueprints come in. By default, blueprint names
equal the class names they manage.
:::php