Software Development Testing

Too often best practices for testing are an after-thought, or a non-thought with respect to the development and deployment of code.

This generally results in shortcuts or bandaids being applied to the SDLC of an organization which can build up over time and become a major constraint on the future product evolution.

With respect to a PHP driven application, my preference is PHPUnit. So briefly a friendly reminder to distinguish your tests by categorization, without opening up a can-of-worms on how to classify a test; we can do that later.

The simplest and easiest ways to get started with this is to organize your tests within PHPUnit, so you can run specific groups of tests along your deployment cycle.

For example, it’s generally a good practice to run your true unit tests on every commit. If they’re written correctly and concisely this shouldn’t introduce a bottleneck on the developers.

Building on that, this is a simple recommended approach for which tests to run along your pipeline:

  1. Unit
    1. On every commit
    2. After/during each pull-request being merged
  2. Integration
    1. On every preprod build; usually I do this with a jenkins/ansible build in a containerized environment that can hit specific api endpoints.
  3. Functional
    1. On every environment deployment. When deployed to env we’ll run tests specific for that environment to verify behaviors for things like billing rules, email filters, etc.
  4. UI
    1. After deployment; when deployed and “launched” we can run automated UI tests to verify behaviors and UI/UX interactions.
    2. These are typically lower importance/severity, so they can generally happen post-build.

Building a Unit Test in Symfony 3 using JWT Token Authentication

When you’re working with JWT Token Authentication and you want to be a well behaved software developer and build out your Unit Tests (in this case we’re using phpunit), it can be somewhat challenging to tackle out of the box. My solution here uses an inherited “service” that generates the token that can be used in any subsequent requests:

In the interest of adhering to the DRY principle, this class exists specifically for the purpose of being called in every unit test as a means of authorizing each request (see below).

You may want to expand upon this to do some additional authorization or validation on the $token response, but in my use case I wanted the request to fail spectacularly if the $token were FALSE, so I’ve left it as such.

Simulating a POST with DateTimeType to a Symfony3 Api with Forms and avoiding `Invalid value for this field`

When building an Api using Symfony3, you’ll be smart to process all input through Forms, and my strong recommendation is to do so via unique form classes for each Entity.

However, when doing so with custom datetime fields, you’ll need to explicitly specify a format so you don’t waste countless hours debugging what you are certain is a proper DateTime!

Using phpunit to validate a simple node creation, the entire request-to-response flow is as follows:

  1. Api Request
  2. Controller
  3. Form
    1. Validation
  4. ORM (Doctrine)
  5. Response

This simple Unit Test is simulating a form submission with a body and post_date field populated for a simple Node entity. If we were creating this in a Controller or elsewhere, we could assign the value of post_date with a simple DateTime object and Symfony would do its magic of parsing it correctly for us.

For example, in the constructor of the Node you’ll see I’m automatically assigning the create_date when the Node is first created.

Unfortunately, when submitting to an Api, the object translation doesn’t happen in a Unit Test as its passing raw encoded (JSON in this case) data. So we need to explicitly enter the date as a string with format that matches exactly what we’re expecting.

The form itself then simply needs to define the post_date field, the widget type and format allowed.

What’s important is that you match the input in your submission precisely to the format specified in the form. Submitting a DateTime` object will not be recognized correctly, nor will any string that does not explicitly match.