Skip to content

Integration Test: Controllers

Fabian Schmengler edited this page Oct 9, 2017 · 10 revisions

These exercises cover tests on a controller level, that test a whole request/response cycle. They simulate a request and inspect the response and/or side effects.

You will learn

  • use the base controller test case
  • use annotations for application state
  • use data fixtures
  • test ACL for backend controllers

Excercise 1: Inspect Response Body

Clone https://github.com/tddwizard/magento2-exercise-contact.git into app/code/TddWizard/ExerciseContact. It contains a module skeleton with one custom model Inquiry. We will write a custom contact form that saves the input in the inquiry table.

First, we need to display the form.

Write a custom controller TddWizard\ExerciseContact\Controller\Form\Index. Start with a test case that extends Magento\TestFramework\TestCase\AbstractController and use dispatch() to call your controller (exercise_contact/form). Write at least one assertion to test if a form is displayed with exercise_contact/form/save as action. The form should contain a text input email and a text area message.

For simple checks where you control the format of the HTML output, regular expressions can be sufficient. But it pays off to use a DOM parser and write custom assertions. Here are some that come in handy and could be put into an abstract base test case or a trait:

protected function assertDomElementContains(string $xpath, string $expectedString, string $message = '')
{
    $dom = $this->getResponseDom();
    $this->assertContains($expectedString, $dom->saveHTML((new \DOMXPath($dom))->query($xpath)->item(0)), $message);
}
protected function assertDomElementPresent(string $xpath, string $message = '')
{
    $this->assertDomElementCount($xpath, 1, $message);
}

protected function assertDomElementCount(string $xpath, int $expectedCount, string $message = '')
{
    $dom = $this->getResponseDom();
    $this->assertEquals($expectedCount, (new \DOMXPath($dom))->query($xpath)->length, $message);
}

private function getResponseDom(): \DOMDocument
{
    $dom = new \DOMDocument();
    \libxml_use_internal_errors(true);
    $dom->loadHTML($this->getResponse()->getBody());
    \libxml_use_internal_errors(false);
    return $dom;
}

You can also use this package to make assertions based on CSS selectors: phpunit/phpunit-dom-assertions

Check out solution-integration-test-1 to see a solution.

Excercise 2: Test Redirect Response

Now write the controller that handles the form, TddWizard\ExerciseContact\Controller\Form\Save.

  • Start by testing that the response is a redirect back to the form.
  • Then test if a success message "We received your inquiry and will contact you shortly" is saved in the session.
  • Also write tests for invalid input (empty message body, invalid email address) and test for appropiate error messages instead of the success message.
  • Optional: save form data in session if there was an error and remove saved form data on success. Also test that the form is prefilled if there is saved data.

Take a look at the assert... methods from the AbstractController test base class, they will be useful.

Do not actually save anything yet, that's part of the next exercise.

Check out solution-integration-test-2 to see a solution.

Excercise 3: Test DB Side Effects

Now it's time to save the posted data. Add assertions to the tests from the previous exercise for:

  • a new inquiry is saved with the posted data
  • nothing is saved if the posted data was invalid

Use the InquiryRepositoryInterface to inspect the database. Try out, how the @magentoDbIsolation enabled annotation affects the test database. Tipp: Truncate inquiry table in setUp method.

Check out solution-integration-test-3 to see a solution.

Excercise 4: Customer Session

For logged in customers, the email address field should not be displayed, and the saved email address should come from the customer account.

Extend your tests for this new case.

Use @magentoDataFixture with one of the fixtures of the core test suite to create a test customer. Use the customer session to set the test customer as logged in.

Check out solution-integration-test-4 to see a solution.

Exercise 5: Admin Controller and ACL

The module already contains generated code for an admin grid. Let's write a simple test case to verify that the grid loads and shows saved inquiries.

Write a new test case that extends Magento\TestFramework\TestCase\AbstractBackendController. This base class takes care of authentication for the admin user.

Use the InquiryRepository to save a new inquiry before dispatching the grid controller.

You will notice that the test case already contains two tests which are marked as incomplete by default:

  • AclHasAccess test is not complete
  • Acl test is not complete

Provide values for the $uri and $resource attributes to automatically test access control.

Check out solution-integration-test-5 to see a solution.