mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge pull request #2443 from dangerdan/doc_corrections
Documentation corrections
This commit is contained in:
commit
3edbfa5b99
@ -17,14 +17,10 @@ The simple usage, Permission::check("PERM_CODE") will detect if the currently lo
|
|||||||
* Call **Permission::check("MY_PERMISSION_CODE")** to see if the current user has MY_PERMISSION_CODE.
|
* Call **Permission::check("MY_PERMISSION_CODE")** to see if the current user has MY_PERMISSION_CODE.
|
||||||
* MY_PERMISSION_CODE can be loaded into the Security admin on the appropriate group, using the "Permissions" tab.
|
* MY_PERMISSION_CODE can be loaded into the Security admin on the appropriate group, using the "Permissions" tab.
|
||||||
|
|
||||||
You can use whatever codes you like, but for the sanity of developers and users, it would be worth listing the codes in
|
|
||||||
[permissions:codes](/reference/permission)
|
|
||||||
|
|
||||||
## PermissionProvider
|
## PermissionProvider
|
||||||
|
|
||||||
`[api:PermissionProvider]` is an interface which lets you define a method *providePermissions()*. This method should return a
|
`[api:PermissionProvider]` is an interface which lets you define a method *providePermissions()*.
|
||||||
map of permission code names with a human readable explanation of its purpose (see
|
This method should return a map of permission code names with a human readable explanation of its purpose.
|
||||||
[permissions:codes](/reference/permission)).
|
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
class Page_Controller implements PermissionProvider {
|
class Page_Controller implements PermissionProvider {
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
# Creating a SilverStripe Test
|
# Creating a SilverStripe Test
|
||||||
|
|
||||||
A test is created by extending one of two classes, SapphireTest and FunctionalTest. You would subclass SapphireTest to
|
A test is created by extending one of two classes, SapphireTest and FunctionalTest.
|
||||||
test your application logic, for example testing the behaviour of one of your `[api:DataObjects]`, whereas FunctionalTest
|
You would subclass `[api:SapphireTest]` to test your application logic,
|
||||||
is extended when you want to test your application's functionality, such as testing the results of GET and POST requests,
|
for example testing the behaviour of one of your `[DataObjects](api:DataObject)`,
|
||||||
and validating the content of a page. `[api:FunctionalTest]` is a subclass of `[api:SapphireTest]`.
|
whereas `[api:FunctionalTest]` is extended when you want to test your application's functionality,
|
||||||
|
such as testing the results of GET and POST requests,
|
||||||
|
and validating the content of a page. FunctionalTest is a subclass of SapphireTest.
|
||||||
|
|
||||||
## Creating a test from SapphireTest
|
## Creating a test from SapphireTest
|
||||||
|
|
||||||
@ -27,11 +29,7 @@ Here is an example of a test which extends SapphireTest:
|
|||||||
'home' => 'home',
|
'home' => 'home',
|
||||||
'staff' => 'my-staff',
|
'staff' => 'my-staff',
|
||||||
'about' => 'about-us',
|
'about' => 'about-us',
|
||||||
'staffduplicate' => 'my-staff-2',
|
'staffduplicate' => 'my-staff-2'
|
||||||
'product1' => '1-1-test-product',
|
|
||||||
'product2' => 'another-product',
|
|
||||||
'product3' => 'another-product-2',
|
|
||||||
'product4' => 'another-product-3',
|
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach($expectedURLs as $fixture => $urlSegment) {
|
foreach($expectedURLs as $fixture => $urlSegment) {
|
||||||
@ -44,10 +42,10 @@ Here is an example of a test which extends SapphireTest:
|
|||||||
Firstly we define a static member `$fixture_file`, this should point to a file that represents the data we want to test,
|
Firstly we define a static member `$fixture_file`, this should point to a file that represents the data we want to test,
|
||||||
represented in YAML. When our test is run, the data from this file will be loaded into a test database for our test to use.
|
represented in YAML. When our test is run, the data from this file will be loaded into a test database for our test to use.
|
||||||
This property can be an array of strings pointing to many .yml files, but for our test we are just using a string on its
|
This property can be an array of strings pointing to many .yml files, but for our test we are just using a string on its
|
||||||
own. For more detail on fixtures, see the [page on fixtures](fixtures).
|
own. For more detail on fixtures, see [this page](fixtures).
|
||||||
|
|
||||||
The second part of our class is the `testURLGeneration` method. This method is our test. You can asign many tests, but
|
The second part of our class is the `testURLGeneration` method. This method is our test. You can asign many tests, but
|
||||||
again for our purposes there is just the one. When the test is executed, methods prefixed with the word **test** will be
|
again for our purposes there is just the one. When the test is executed, methods prefixed with the word `test` will be
|
||||||
run. The test database is rebuilt every time one of these methods is run.
|
run. The test database is rebuilt every time one of these methods is run.
|
||||||
|
|
||||||
Inside our test method is the `objFromFixture` method that will generate an object for us based on data from our fixture
|
Inside our test method is the `objFromFixture` method that will generate an object for us based on data from our fixture
|
||||||
@ -57,10 +55,10 @@ database. This means that you can use it to test the functions responsible for l
|
|||||||
|
|
||||||
The final part of our test is an assertion command, `assertEquals`. An assertion command allows us to test for something
|
The final part of our test is an assertion command, `assertEquals`. An assertion command allows us to test for something
|
||||||
in our test methods (in this case we are testing if two values are equal). A test method can have more than one assertion
|
in our test methods (in this case we are testing if two values are equal). A test method can have more than one assertion
|
||||||
command, and if anyone of these tests fail, then the whole test method will fail.
|
command, and if any one of these assertions fail, so will the test method.
|
||||||
|
|
||||||
For more information on PHPUnit's assertions see the [PHPUnit manual](http://www.phpunit.de/manual/current/en/api.html#api.assert).
|
For more information on PHPUnit's assertions see the [PHPUnit manual](http://www.phpunit.de/manual/current/en/api.html#api.assert).
|
||||||
|
|
||||||
The `[api:SapphireTest]` class comes with additional assertions which are more specific to the Sapphire, for example the
|
The `[api:SapphireTest]` class comes with additional assertions which are more specific to Sapphire, for example the
|
||||||
`[assertEmailSent](api:SapphireTest->assertEmailSent())` method, which simulates sending emails through the `Email->send()`
|
`assertEmailSent` method, which simulates sending emails through the `Email->send()`
|
||||||
API without actually using a mail server. For more details on this see th [testing emails](testing-email)) guide.
|
API without actually using a mail server. For more details on this see the [testing emails](testing-email) guide.
|
||||||
|
@ -2,80 +2,126 @@
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Often you need to test your functionality with some existing data, so called "fixtures".
|
You will often find the need to test your functionality with some consistent data.
|
||||||
The `[api:SapphireTest]` class already prepares an empty database for you,
|
If we are testing our code with the same data each time,
|
||||||
and you have various ways to define those fixtures.
|
we can trust our tests to yeild reliable results.
|
||||||
|
In Silverstripe we define this data via 'fixtures' (so called because of their fixed nature).
|
||||||
|
The `[api:SapphireTest]` class takes care of populating a test database with data from these fixtures -
|
||||||
|
all we have to do is define them, and we have a few ways in which we can do this.
|
||||||
|
|
||||||
## YAML Fixtures
|
## YAML Fixtures
|
||||||
|
|
||||||
YAML is a markup language which is deliberately simple and easy to read,
|
YAML is a markup language which is deliberately simple and easy to read,
|
||||||
so ideal for our fixture generation.
|
so it is ideal for fixture generation.
|
||||||
|
|
||||||
We will begin with a sample file and talk our way through it.
|
Say we have the following two DataObjects:
|
||||||
|
|
||||||
Page:
|
:::php
|
||||||
home:
|
class Player extends DataObject {
|
||||||
Title: Home
|
static $db = array (
|
||||||
about:
|
'Name' => 'Varchar(255)'
|
||||||
Title: About Us
|
);
|
||||||
staff:
|
|
||||||
Title: Staff
|
|
||||||
URLSegment: my-staff
|
|
||||||
Parent: =>Page.about
|
|
||||||
|
|
||||||
RedirectorPage:
|
static $has_one = array(
|
||||||
redirect_home:
|
'Team' => 'Team'
|
||||||
RedirectionType: Internal
|
);
|
||||||
LinkTo: =>Page.home
|
}
|
||||||
|
|
||||||
|
class Team extends DataObject {
|
||||||
|
static $db = array (
|
||||||
|
'Name' => 'Varchar(255)',
|
||||||
|
'Origin' => 'Varchar(255)'
|
||||||
|
);
|
||||||
|
|
||||||
The contents of the YAML file are broken into three levels.
|
static $has_many = array(
|
||||||
|
'Players' => 'Player'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
* **Top level: class names** - `Page` and `RedirectorPage`. This is the name of the dataobject class that should be created.
|
We can represent multiple instances of them in `YAML` as follows:
|
||||||
The fact that `RedirectorPage` is actually a subclass is irrelevant to the system populating the database. It just
|
|
||||||
instantiates the object you specify.
|
|
||||||
* **Second level: identifiers** - `home`, `about`, etc. These are the identifiers that you pass as
|
|
||||||
the second argument of SapphireTest::objFromFixture(). Each identifier you specify delimits a new database record.
|
|
||||||
This means that every record needs to have an identifier, whether you use it or not.
|
|
||||||
* **Third level: fields** - each field for the record is listed as a 3rd level entry. In most cases, the field's raw
|
|
||||||
content is provided. However, if you want to define a relationship, you can do so using "=>".
|
|
||||||
|
|
||||||
There are a couple of lines like this:
|
:::yml
|
||||||
|
Player:
|
||||||
|
john:
|
||||||
|
Name: John
|
||||||
|
Team: =>Team.hurricanes
|
||||||
|
joe:
|
||||||
|
Name: Joe
|
||||||
|
Team: =>Team.crusaders
|
||||||
|
jack:
|
||||||
|
Name: Jack
|
||||||
|
Team: =>Team.crusaders
|
||||||
|
Team:
|
||||||
|
hurricanes:
|
||||||
|
Name: The Hurricanes
|
||||||
|
Origin: Wellington
|
||||||
|
crusaders:
|
||||||
|
Name: The Crusaders
|
||||||
|
Origin: Bay of Plenty
|
||||||
|
|
||||||
Parent: =>Page.about
|
Our `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 for the test.
|
||||||
|
|
||||||
This will tell the system to set the ParentID database field to the ID of the Page object with the identifier "about".
|
The second level, `john`/`joe`/`jack` & `hurricanes`/`crusaders`, are identifiers.
|
||||||
This can be used on any has-one or many-many relationship. Note that we use the name of the relationship (Parent), and
|
These are what you pass as the second argument of `SapphireTest::objFromFixture()`.
|
||||||
not the name of the database field (ParentID)
|
Each identifier you specify represents a new object.
|
||||||
|
|
||||||
On many-many relationships, you should specify a comma separated list of values.
|
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 seen by the fields prefixed with `=>`.
|
||||||
|
|
||||||
MyRelation: =>Class.inst1,=>Class.inst2,=>Class.inst3
|
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.
|
||||||
|
Take the player John for example, his team is the Hurricanes which is represented by `=>Team.hurricanes`.
|
||||||
|
This is tells the system that we want to set up a relationship for the `Player` object `john` with the `Team` object `hurricanes`.
|
||||||
|
It will populate the `Player` object's `TeamID` with the ID of `hurricanes`,
|
||||||
|
just like how a relationship is always set up.
|
||||||
|
|
||||||
An crucial thing to note is that **the YAML file specifies DataObjects, not database records**. The database is
|
<div class="hint" markdown='1'>
|
||||||
populated by instantiating DataObject objects, setting the fields listed, and calling write(). This means that any
|
Note that we use the name of the relationship (Team), and not the name of the database field (TeamID).
|
||||||
onBeforeWrite() or default value logic will be executed as part of the test. This forms the basis of our
|
</div>
|
||||||
testURLGeneration() test above.
|
|
||||||
|
|
||||||
For example, the URLSegment value of Page.staffduplicate is the same as the URLSegment value of Page.staff. When the
|
This style of relationship declaration can be used for both a `has-one` and a `many-many` relationship.
|
||||||
fixture is set up, the URLSegment value of Page.staffduplicate will actually be my-staff-2.
|
For `many-many` relationships, we specify a comma separated list of values.
|
||||||
|
For example we could just as easily write the above as:
|
||||||
|
|
||||||
Finally, be aware that requireDefaultRecords() is **not** called by the database populator - so you will need to specify
|
:::yml
|
||||||
standard pages such as 404 and home in your YAML file.
|
Player:
|
||||||
|
john:
|
||||||
|
Name: John
|
||||||
|
joe:
|
||||||
|
Name: Joe
|
||||||
|
jack:
|
||||||
|
Name: Jack
|
||||||
|
Team:
|
||||||
|
hurricanes:
|
||||||
|
Name: The Hurricanes
|
||||||
|
Origin: Wellington
|
||||||
|
Players: =>Player.john
|
||||||
|
crusaders:
|
||||||
|
Name: The Crusaders
|
||||||
|
Origin: Bay of Plenty
|
||||||
|
Players: =>Player.joe,=>Player.jack
|
||||||
|
|
||||||
|
A crucial thing to note is that **the YAML file specifies DataObjects, not database records**.
|
||||||
|
The database is populated by instantiating DataObject objects and setting the fields declared in the YML,
|
||||||
|
then calling write() on those objects.
|
||||||
|
This means that any `onBeforeWrite()` or default value logic will be executed as part of the test.
|
||||||
|
The reasoning behind this is to allow us to test the `onBeforeWrite` functionality of our objects.
|
||||||
|
You can see this kind of testing in action in the `testURLGeneration()` test from the example in
|
||||||
|
[Creating a SilverStripe Test](creating-a-silverstripe-test).
|
||||||
|
|
||||||
## Test Class Definition
|
## Test Class Definition
|
||||||
|
|
||||||
|
### Manual Object Creation
|
||||||
|
|
||||||
|
Sometimes statically defined fixtures don't suffice. This could be because of the complexity of the tested model,
|
||||||
## Manual Object Creation
|
or because the YAML format doesn't allow you to modify all of a model's state.
|
||||||
|
|
||||||
Sometimes statically defined fixtures don't suffice, because of the complexity of the tested model,
|
|
||||||
or because the YAML format doesn't allow you to modify all model state.
|
|
||||||
One common example here is publishing pages (page fixtures aren't published by default).
|
One common example here is publishing pages (page fixtures aren't published by default).
|
||||||
|
|
||||||
You can always resort to creating objects manually in the test setup phase.
|
You can always resort to creating objects manually in the test setup phase.
|
||||||
Since the test database is cleared on every test method, you'll get a fresh
|
Since the test database is cleared on every test method, you'll get a fresh set of test instances every time.
|
||||||
set of test instances every time.
|
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
class SiteTreeTest extends SapphireTest {
|
class SiteTreeTest extends SapphireTest {
|
||||||
@ -94,20 +140,21 @@ set of test instances every time.
|
|||||||
|
|
||||||
### Why Factories?
|
### Why Factories?
|
||||||
|
|
||||||
Manually defined fixture provide full flexibility, but 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
|
Alternatively, you can use the `[api:FixtureFactory]` class, which allows you to set default values,
|
||||||
to set default values, callbacks on object creation, and dynamic/lazy value setting.
|
callbacks on object creation, and dynamic/lazy value setting.
|
||||||
By the way, the `SapphireTest` YAML fixtures rely on internally on this class as well.
|
|
||||||
|
|
||||||
The idea is that rather than instanciating objects directly, we'll have a factory class for them.
|
<div class="hint" markdown='1'>
|
||||||
This factory can have so called "blueprints" defined on it, which tells the factory
|
SapphireTest uses FixtureFactory under the hood when it is provided with YAML based fixtures.
|
||||||
how to instanciate an object of a specific type. Blueprints need a name,
|
</div>
|
||||||
which is usually set to the class it creates.
|
|
||||||
|
The idea is that rather than instantiating objects directly, we'll have a factory class for them.
|
||||||
|
This factory can have so called "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.
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
Since blueprints are auto-created for all available DataObject subclasses,
|
Since blueprints are auto-created for all available DataObject subclasses,
|
||||||
you only need to instanciate a factory to start using it.
|
you only need to instantiate a factory to start using it.
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
$factory = Injector::inst()->create('FixtureFactory');
|
$factory = Injector::inst()->create('FixtureFactory');
|
||||||
@ -124,7 +171,7 @@ In order to create an object with certain properties, just add a second argument
|
|||||||
:::php
|
:::php
|
||||||
$obj = $factory->createObject('MyClass', 'myobj1', array('MyProperty' => 'My Value'));
|
$obj = $factory->createObject('MyClass', 'myobj1', array('MyProperty' => 'My Value'));
|
||||||
|
|
||||||
### Default Properties
|
#### Default Properties
|
||||||
|
|
||||||
Blueprints can be overwritten in order to customize their behaviour,
|
Blueprints can be overwritten in order to customize their behaviour,
|
||||||
for example with default properties in case none are passed into `createObject()`.
|
for example with default properties in case none are passed into `createObject()`.
|
||||||
@ -134,11 +181,10 @@ for example with default properties in case none are passed into `createObject()
|
|||||||
'MyProperty' => 'My Default Value'
|
'MyProperty' => 'My Default Value'
|
||||||
));
|
));
|
||||||
|
|
||||||
### Dependent Properties
|
#### Dependent Properties
|
||||||
|
|
||||||
Values can be set on demand through anonymous functions,
|
Values can be set on demand through anonymous functions, which can either generate random defaults,
|
||||||
which can either generate random defaults, or create
|
or create composite values based on other fixture data.
|
||||||
composite values based on other fixture data.
|
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
$factory->define('Member', array(
|
$factory->define('Member', array(
|
||||||
@ -152,7 +198,7 @@ composite values based on other fixture data.
|
|||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
### Relations
|
#### Relations
|
||||||
|
|
||||||
Model relations can be expressed through the same notation as in the YAML fixture format
|
Model relations can be expressed through the same notation as in the YAML fixture format
|
||||||
described earlier, through the `=>` prefix on data values.
|
described earlier, through the `=>` prefix on data values.
|
||||||
@ -162,7 +208,7 @@ described earlier, through the `=>` prefix on data values.
|
|||||||
'MyHasManyRelation' => '=>MyOtherObject.obj1,=>MyOtherObject.obj2'
|
'MyHasManyRelation' => '=>MyOtherObject.obj1,=>MyOtherObject.obj2'
|
||||||
));
|
));
|
||||||
|
|
||||||
### Callbacks
|
#### Callbacks
|
||||||
|
|
||||||
Sometimes new model instances need to be modified in ways which can't be expressed
|
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.
|
in their properties, for example to publish a page, which requires a method call.
|
||||||
|
@ -31,7 +31,7 @@ test.
|
|||||||
**Fake Object**: A substitute object that simply replaces a real object with the same interface, and returns a
|
**Fake Object**: A substitute object that simply replaces a real object with the same interface, and returns a
|
||||||
pre-determined (usually fixed) value from each method.
|
pre-determined (usually fixed) value from each method.
|
||||||
|
|
||||||
**Mock Object:** A substitute object that mimicks the same behavior as a real object (some people think of mocks as
|
**Mock Object:** A substitute object that mimics the same behavior as a real object (some people think of mocks as
|
||||||
"crash test dummy" objects). Mocks differ from other kinds of substitute objects in that they must understand the
|
"crash test dummy" objects). Mocks differ from other kinds of substitute objects in that they must understand the
|
||||||
context of each call to them, setting expectations of which, and what order, methods will be invoked and what parameters
|
context of each call to them, setting expectations of which, and what order, methods will be invoked and what parameters
|
||||||
will be passed.
|
will be passed.
|
||||||
|
@ -111,7 +111,7 @@ All command-line arguments are documented on
|
|||||||
### Via the "sake" Wrapper on Command Line
|
### Via the "sake" Wrapper on Command Line
|
||||||
|
|
||||||
The [sake](/topics/commandline) executable that comes with SilverStripe can trigger a customized
|
The [sake](/topics/commandline) executable that comes with SilverStripe can trigger a customized
|
||||||
"[api:TestRunner]" class that handles the PHPUnit configuration and output formatting.
|
`[api:TestRunner]` class that handles the PHPUnit configuration and output formatting.
|
||||||
While the custom test runner a handy tool, its also more limited than using `phpunit` directly,
|
While the custom test runner a handy tool, its also more limited than using `phpunit` directly,
|
||||||
particularly around formatting test output.
|
particularly around formatting test output.
|
||||||
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
# Testing Email
|
# Testing Email
|
||||||
|
|
||||||
SilverStripe's test system has built-in support for testing emails sent using the Email class.
|
SilverStripe's test system has built-in support for testing emails sent using the `[api:Email]` class.
|
||||||
|
|
||||||
## How it works
|
## How it works
|
||||||
|
|
||||||
For this to work, you need to send emails using the `Email` class, which is generally the way that we recommend you
|
For this to work, you need to send emails using the `Email` class,
|
||||||
send emails in your SilverStripe application. Here is a simple example of how you might do this:
|
which is generally the way that we recommend you send emails in your SilverStripe application.
|
||||||
|
Here is a simple example of how you might do this:
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
$e = new Email();
|
$e = new Email();
|
||||||
@ -14,15 +15,14 @@ send emails in your SilverStripe application. Here is a simple example of how y
|
|||||||
$e->Body = "I just really wanted to email you and say hi.";
|
$e->Body = "I just really wanted to email you and say hi.";
|
||||||
$e->send();
|
$e->send();
|
||||||
|
|
||||||
|
Normally, the `send()` method would send an email using PHP's `mail()` function.
|
||||||
Normally, the send() method would send an email using PHP's mail() function. However, if you are running a `[api:SapphireTest]`
|
However, if you are running a `[api:SapphireTest]` test, then it holds off actually sending the email,
|
||||||
test, then it holds off actually sending the email, and instead lets you assert that an email was sent using this method.
|
and instead lets you assert that an email was sent using this method.
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
$this->assertEmailSent("someone@example.com", null, "/th.*e$/");
|
$this->assertEmailSent("someone@example.com", null, "/th.*e$/");
|
||||||
|
|
||||||
|
The arguments are `$to`, `$from`, `$subject`, `$body`, and can take one of the three following types:
|
||||||
The arguments are `$to`, `$from`, `$subject`, `$body`, and can be take one of the following three forms:
|
|
||||||
|
|
||||||
* A string: match exactly that string
|
* A string: match exactly that string
|
||||||
* `null/false`: match anything
|
* `null/false`: match anything
|
||||||
@ -31,18 +31,16 @@ The arguments are `$to`, `$from`, `$subject`, `$body`, and can be take one of th
|
|||||||
## How to use it
|
## How to use it
|
||||||
|
|
||||||
Given all of that, there is not a lot that you have to do in order to test emailing functionality in your application.
|
Given all of that, there is not a lot that you have to do in order to test emailing functionality in your application.
|
||||||
|
Whenever we include e-mailing functionality in our application,
|
||||||
* Write your SilverStripe application, using the Email class to send emails.
|
we simply use `$this->assertEmailSent()` to check our mail has been passed to PHP `mail` in our tests.
|
||||||
* Write tests that trigger the email sending functionality.
|
|
||||||
* Include appropriate `$this->assertEmailSent()` calls in those tests.
|
|
||||||
|
|
||||||
That's it!
|
That's it!
|
||||||
|
|
||||||
## What isn't tested
|
## What isn't tested
|
||||||
|
|
||||||
It's important to realise that this email testing doesn't actually test everything that there is to do with email. The
|
It's important to realise that this email testing doesn't actually test everything that there is to do with email.
|
||||||
focus of this email testing system is testing that your application is triggering emails correctly. It doesn't test
|
The focus of this email testing system is testing that your application is triggering emails correctly.
|
||||||
your email infrastructure outside of the webserver. For example:
|
It doesn't test your email infrastructure outside of the webserver. For example:
|
||||||
|
|
||||||
* It won't test that email is correctly configured on your webserver
|
* It won't test that email is correctly configured on your webserver
|
||||||
* It won't test whether your emails are going to be lost in someone's spam filter
|
* It won't test whether your emails are going to be lost in someone's spam filter
|
||||||
@ -50,9 +48,9 @@ your email infrastructure outside of the webserver. For example:
|
|||||||
|
|
||||||
## How it's built
|
## How it's built
|
||||||
|
|
||||||
For those of you who want to dig a little deeper, here's a quick run-through of how the system has been built. As well
|
For those of you who want to dig a little deeper, here's a quick run-through of how the system has been built.
|
||||||
as explaining how we built the email test, this is a good design pattern for making other "tricky external systems"
|
As well as explaining how we built the email test,
|
||||||
testable:
|
this is a good design pattern for making other "tricky external systems" testable:
|
||||||
|
|
||||||
1. The `Email::send()` method makes uses of a static object, `Email::$mailer`, to do the dirty work of calling
|
1. The `Email::send()` method makes uses of a static object, `Email::$mailer`, to do the dirty work of calling
|
||||||
mail(). The default mailer is an object of type `Mailer`, which performs a normal send.
|
mail(). The default mailer is an object of type `Mailer`, which performs a normal send.
|
||||||
|
Loading…
Reference in New Issue
Block a user