Skip to main content

Securing Platform Stability With Acceptance Testing

How we ensure that our permission models on the platform work as expected? With Acceptance tests we test these scenarios and revealed a couple of bugs already. They help us to prevent leaking data to non-authorised people. This article describes how we do these tests.

Building a brand new service means a lot of changes in the beginning. Definitions change, permission models change, and modules get refactored often. At some point we got the feeling that we’re not sure if all the permissions were implemented as per definition. For some components we realised that we didn’t even have a definition at all. Writing Acceptance tests helps us building a more robust platform.

We have a granular permission model for Colloq, and we definitely want to ensure that all permissions are only given as expected. It’s not practicable to manually test user permissions by logging in and out again. Instead, we wanted to have these tests run automatically.

Codeception

Our backend code stack builds upon PHP. Codeception runs as the ideal candidate for a unified testing stack. It can handle Unit tests, user-driven Acceptance tests and comes with a very useful PhpBrowser built-in. With this framework it’s easy to write tests, even for people who aren’t proficient in PHP. And with its built-in PhpBrowser it’s fairly fast as well.

Let’s look at a real test

To show you how it works, let’s dig into a sample acceptance test. Say we want to limit access to our “create speaker” page. It should only be available to Administrators and people who organise the event. This is our test file for the scenario:

<?php

use Step\Acceptance\Admin;
use Step\Acceptance\Organizer;
use Step\Acceptance\Staff;
use Step\Acceptance\User;
use Step\Acceptance\Visitor;

class EventSpeakerCreateCest {
  protected $url = '/events/eventseries/2017/city/speakers;create';

  protected function pageIsVisible( AcceptanceTester $I ) {
    $I->wantTo( 'see the create speaker page' );
    $I->amOnPage( $this->url );
    $I->see( 'Add a speaker' );
  }

  protected function pageIsDenied( AcceptanceTester $I ) {
    $I->wantTo( 'be denied to see the speaker create page' );
    $I->amDeniedToSeePage( $this->url );
  }

  public function adminTest( Admin $admin ) {
    $this->pageIsVisible( $admin );
  }

  public function organizerTest( Organizer $organizer ) {
    $this->pageIsVisible( $organizer );
  }

  public function staffTest( Staff $staff ) {
    $this->pageIsVisible( $staff );
  }

  public function userTest( User $user ) {
    $this->pageIsVisible( $user );
  }

  public function visitorTest( Visitor $visitor ) {
    $visitor->wantTo( 'be denied to see the speaker create page' );
    $visitor->amRedirectedToLoginForPage( $this->url );
  }
}

In the example above you can see that we define the URL once and include very little static information. You can see that we don’t use the default Codeception functions, such as $I->amOnPage(‘/login’);. Instead, we use abstract classes and custom helpers to inject, for example, the login action for a logged-in user. Usually, following Codeception’s defaults, you’d write this scenario like this:

<?php

$I->amOnPage('/login');
$I->fillField('username', 'user');
$I->fillField('password', 'qwerty');
$I->click('Login');
$I->see('Welcome, User!');

With our new helper classes, we now can write our tests in a much simpler way:

public function userTest( User $user ) {
  $this->pageIsVisible( $user );
}

For permission testing, this is a much easier and safer way to write tests. With 5-10 permission tests for each module, we don’t have to repeat ourselves anymore.

The Future

The vendor of Codeception also offers CodeceptJS, a JavaScript testing framework. We’re currently considering it for our JavaScript/Node.js services and scripts. We haven’t evaluated it yet, but having testing frameworks from the same vendor would be a great solution.

Reflecting

We found Acceptance Tests to be a good friend to improve the stability of our growing platform, and Codeception is our tool of choice.

Looking back, we could have avoided and resolved some errors earlier. As we wrote more tests, we changed data models that didn’t comply to the required permissions. Writing and running these acceptance tests has made our development process easier and results in a more stable platform. Without these automated tests, this would be much more difficult, not to say almost impossible. Following this experience, we now include tests in any new feature right away, which gives us the confidence that everything works the way we’d expect it to.