Login - Part 2 - Being Careful of Edge Cases


In this video we add in all the potential unhappy paths we can think of for our Symfony 3 API login journey.

There's always one happy path. There's always at least one unhappy path. The pessimism is high.

Our login.feature file now looks like this:

# /src/AppBundle/Features/login.feature

Feature: Handle user login via the RESTful API

  In order to allow secure access to the system
  As a client software developer
  I need to be able to let users log in and out

  Background:
    Given there are Users with the following details:
      | id | username | email          | password |
      | 1  | peter    | peter@test.com | testpass |
      | 2  | john     | john@test.org  | johnpass |
     And I set header "Content-Type" with value "application/json"

  Scenario: Cannot GET Login
    When I send a "GET" request to "/login"
    Then the response code should be 405

  Scenario: User cannot Login with bad credentials
    When I send a "POST" request to "/login" with body:
      """
      {
        "username": "jimmy",
        "password": "badpass"
      }
      """
    Then the response code should be 401

  Scenario: User can Login with good credentials (username)
    When I send a "POST" request to "/login" with body:
      """
      {
        "username": "peter",
        "password": "testpass"
      }
      """
    Then the response code should be 200
     And the response should contain "token"

  Scenario: User can Login with good credentials (email)
    When I send a "POST" request to "/login" with body:
      """
      {
        "username": "peter@test.com",
        "password": "testpass"
      }
      """
    Then the response code should be 200
     And the response should contain "token"

Ok, so positive outcomes at this stage?

We have a full set of passing tests!

Yes, we didn't have to write more code. This was to test all the other various states we anticipate our code having to respond too.

This is different to unit testing. This is writing tests to cover business needs. These are acceptance tests.

We've got the basis of our entire testing setup here.

We've covered most of the test step definitions we will use throughout the rest of this series. We've send data into, and had responses from our API.

These are big steps. The foundation of our system is in place.

Also, we can seed the database. We can define 'stuff' and ensure that the DB state is as expected at the start of our test cycles. This makes things much slower than typical unit testing. Real life doesn't come with the 350% time compressing I do in post-production for the videos ;)

Behat + Symfony 3 Profiler Tip

A really nice way to use Behat and Symfony 3 (or 2) together is in using the _profiler route to really dig into the request through response lifecycle.

The way I do this is to annotate / tag my tests with @this, and then run behat with the --tags flag:

php vendor/bin/behat --tags=this

And you only run the single test you care about.

Actually, you can tag multiple tests in this way and run subsets, which is another approach to using tags that's equally useful.

Then, when you've run your test, browse the response headers and grab the X-Debug-Token-Link header value:

X-Debug-Token-Link →http://api.rest-user-api.dev/app_acceptance.php/_profiler/8612ce

Browsing that takes you directly to the profiler for that exact request. Amazingly useful, and should be a major help in debugging any issues you run in too.

Code For This Course

Get the code for this course.

Episodes