# Tutorial: Testing Form Submissions ## Overview In this tutorial we'll show you how to test the submission of a SilverStripe form by remote controlling a browser. Along the way, you'll learn how to create database fixtures and make assertions about any changed state in the database. In order to illustrate these concepts, we'll create a "report this page" feature which shows at the bottom of each page, and gives its visitors an opportunity to help discover and fix problems with its content. The "report" consists of a form with a dropdown, its submission is stored in a custom SilverStripe database object. ![](https://www.monosnap.com/image/Xa94a2DBdcrZ21mKYVzTGXCHF.png) ## Preparation First of all, check out a default SilverStripe project and ensure it runs on your environment. Detailed installation instructions can be found in the [README](../README.md) of this module. Once you've got the SilverStripe project running, make sure you've started Selenium. With all configuration in place, initialize Behat for vendor/bin/behat --init @mysite This will create a location for our feature files later on, in `mysite/tests/behat/features`. Note that the module doesn't need its own `behat.yml` configuration file since it reuses the one in the root folder. ## Feature Specification One major goal for "testing by example" through Behat is bringing the tests closer to the whole team, by making them part of the agile process and ideally have the customer write some tests in his own language (read more about this concept at [dannorth.net](http://dannorth.net/whats-in-a-story/)). In this spirit, we'll start "from the outside in", and write our features before implementing them. A first draft might look something like the following: ```cucumber Feature: Report Abuse As a website user I want to report inappropriate content In order to maintain high quality content Scenario: Report abuse through preselected options Given I go to a page Then I should see "Report this page" When I select "Outdated" And I press the button Then I should see "Thanks for your submission" ``` The "syntax" conventions used here are called the ["Gherkin" language](https://github.com/cucumber/cucumber/wiki/Gherkin). It is fairly free-form, with only few rules about indentation and keywords such as `Feature:`, `Scenario:` or `Given`. Each of the actual steps underneath `Scenario` needs to map to logic which knows how to tell the browser what to do. Let's try to run our scenario: vendor/bin/behat --ansi @mysite We'll see all steps marked as "not implemented" (orange). Thankfully Behat already comes with a lot of step definitions, so our next move is to review what's already available: vendor/bin/behat --di @mysite The step definitions include form interactions, so we only need to adjust our steps a bit to make them executable. ```cucumber Scenario: Report abuse through preselected options Given I go to a page Then I should see "Report this page" When I select "Outdated" from "Reason" And I press "Submit Report" Then I should see "Thanks for your submission" ``` This type of refactoring is quite common, since step definitions are ideally abstracted and shared between features. In this case, we needed to make some steps less ambiguous, for example referencing which form field to select from. We haven't written the code for this form field yet, but its easy enough to label it "Reason" without knowing much about its implementation. Run the tests again, and you should see some steps marked as "skipped" instead of "not implemented". There's still a bit of ambiguity in our feature: Each test run starts with a clean database, meaning there's no pages to open in a browser either. Let's fix this by defining one, and asking Behat to open it: ```cucumber Scenario: Report abuse through preselected options Given a "page" "My Page" Given I go to the "page" "My Page" ... ``` ## SilverStripe Code Enough theory, we have a good idea of what our feature should do, let's get down to coding! We won't get too much into details, but overall we're creating a new `PageAbuseReport` class with a `has_one` relationship to `Page`. This new object gets written by a form generated through `Page_Controller->ReportForm()`. ```php // mysite/code/Page.php class Page extends SiteTree { private static $has_many = array('PageAbuseReports' => 'PageAbuseReport'); } class Page_Controller extends ContentController { // ... private static $allowed_actions = array('ReportForm'); public function ReportForm() { return new Form( $this, 'ReportForm', new FieldList( new HeaderField('ReportTitle', 'Report this page', 3), (new DropdownField('Reason', 'Reason')) ->setSource(array( 'Inappropriate' => 'Inappropriate', 'Outdated' => 'Outdated', 'Misleading' => 'Misleading', )) ), new FieldList( new FormAction('doReport', 'Submit Report') ) ); } public function doReport($data, $form) { (new PageAbuseReport(array( 'Reason' => $data['Reason'], 'PageID' => $this->ID, )))->write(); $form->sessionMessage('Thanks for your submission!', 'good'); return $this->redirectBack(); } } ``` ```php // mysite/code/PageAbuseReport.php class PageAbuseReport extends DataObject { private static $db = array('Reason' => 'Text'); private static $has_one = array('Page' => 'Page'); } ``` Now we just need to render the form on every page, by placing it at the bottom of `themes/simple/templates/Layout/Page.ss`: ```html