Simple HTTP smoke testing for your Symfony application

Overview

Shopsys HTTP Smoke Testing

Build Status Downloads

This package enables you to do simple HTTP smoke testing of your Symfony application.

Basically, it generates a HTTP request for every page (controller action) provided by the application router and then asserts that the returned HTTP response code is correct.

While this is not a very sophisticated check, it can answer the essential question "does it run?". It prevents you from triggering 500 Server Error on some seemingly unrelated page when you are doing changes in shared code. Moreover, after initial configuration it is almost maintenance-free as it checks any new routes automatically.

This repository is maintained by shopsys/shopsys monorepo, information about changes is in monorepo CHANGELOG.md.

Installation

Add the package to require-dev in your application:

composer require --dev shopsys/http-smoke-testing

This package internally uses PHPUnit to run the tests. That means that you need to setup your phpunit.xml properly. Fortunately, Symfony comes with example configuration. Renaming the phpunit.xml.dist in your project root (or app/phpunit.xml.dist on Symfony 2) should be sufficient.

Note: If you did not find the file in your project check out the example in Symfony Standard Edition.

Usage

Create new PHPUnit test extending \Shopsys\HttpSmokeTesting\HttpSmokeTestCase class and implement customizeRouteConfigs method.

You can run your new test by:

php vendor/bin/phpunit tests/AppBundle/Smoke/SmokeTest.php

(or php bin/phpunit -c app/phpunit.xml src/AppBundle/Tests/Smoke/SmokeTest.php on Symfony 2)

Warning: This package checks all routes by making real requests. It is important not to execute it on production data. You may unknowingly delete or modify your data or real requests on 3rd party services. Even if you implement some way of protecting the application from side-effect (eg. database transaction wrapping) you should never execute tests on production data.

Example test class

customizeByRouteName('acme_demo_secured_hello', function (RouteConfig $config, RouteInfo $info) { // You can customize RouteConfig to use authentication for secured routes $config->changeDefaultRequestDataSet('Log in as "user".') ->setAuth(new BasicHttpAuth('user', 'userpass')); }); } /** * @param \Symfony\Component\HttpFoundation\Request $request * @return \Symfony\Component\HttpFoundation\Response */ protected function handleRequest(Request $request) { $entityManager = self::$kernel->getContainer()->get('doctrine.orm.entity_manager'); // Enclose request handling in rolled-back database transaction to prevent side-effects $entityManager->beginTransaction(); $response = parent::handleRequest($request); $entityManager->rollback(); return $response; } }">
namespace Tests\AppBundle\Smoke;

use Shopsys\HttpSmokeTesting\Auth\BasicHttpAuth;
use Shopsys\HttpSmokeTesting\HttpSmokeTestCase;
use Shopsys\HttpSmokeTesting\RouteConfig;
use Shopsys\HttpSmokeTesting\RouteConfigCustomizer;
use Shopsys\HttpSmokeTesting\RouteInfo;
use Symfony\Component\HttpFoundation\Request;

class SmokeTest extends HttpSmokeTestCase {
    /**
     * @param \Shopsys\HttpSmokeTesting\RouteConfigCustomizer $routeConfigCustomizer
     */
    protected function customizeRouteConfigs(RouteConfigCustomizer $routeConfigCustomizer)
    {
        $routeConfigCustomizer
            ->customize(function (RouteConfig $config, RouteInfo $info) {
                // This function will be called on every RouteConfig provided by RouterAdapter
                if ($info->getRouteName()[0] === '_') {
                    // You can use RouteConfig to change expected behavior or skip testing particular routes
                    $config->skipRoute('Route name is prefixed with "_" meaning internal route.');
                }
            })
            ->customizeByRouteName('acme_demo_secured_hello', function (RouteConfig $config, RouteInfo $info) {
                // You can customize RouteConfig to use authentication for secured routes
                $config->changeDefaultRequestDataSet('Log in as "user".')
                    ->setAuth(new BasicHttpAuth('user', 'userpass'));
            });
    }

    /**
     * @param \Symfony\Component\HttpFoundation\Request $request
     * @return \Symfony\Component\HttpFoundation\Response
     */
    protected function handleRequest(Request $request)
    {
        $entityManager = self::$kernel->getContainer()->get('doctrine.orm.entity_manager');

        // Enclose request handling in rolled-back database transaction to prevent side-effects
        $entityManager->beginTransaction();
        $response = parent::handleRequest($request);
        $entityManager->rollback();

        return $response;
    }
}

Documentation

By default the test makes request to every route without using any authentication or providing any parameters and expects the response to have HTTP status code 200 OK.

To change this behavior you must implement method customizeRouteConfigs(RouteConfigCustomizer $routeConfigCustomizer) in your test.

RouteConfigCustomizer provides two methods for customizing individual route requests:

  • customize accepts callback function (RouteConfig $config, RouteInfo $info) {...} as the only argument. This is called with each RouteConfig along with RouteInfo collected from your router.
    This method is useful when you want to define general rules for multiple routes (eg. skip all routes with name starting with underscore).
  • customizeByRouteName accepts a single route name or an array of route names as the first argument and same callback as customize as the second argument. This is called with each RouteConfig along with RouteInfo with matching route name. If matching route config is not found a RouteNameNotFoundException is thrown.
    This method is useful when you want to define rules for specific routes (eg. logging in to some secured route).

In your customizing callback you can call three methods on RouteConfig to change the tested behavior:

  • skipRoute can be called to skip this route during test.
  • changeDefaultRequestDataSet is the main method for configuring routes. It returns RequestDataSet object offering the setters needed to change the actual behavior:
    • setExpectedStatusCode changes the expected response HTTP status code that will be asserted.
    • setAuth changes the authentication method for the route. (Use NoAuth for anonymous access, BasicHttpAuth for logging in via basic http headers or implement your own method using AuthInterface.)
    • setParameter specifies value of a route parameter by name.
    • addCallDuringTestExecution adds a callback function (RequestDataSet $requestDataSet, ContainerInterface $container) { ... } to be called before test execution.
      (Useful for code that needs to access the same instance of container as the test method, eg. adding CSRF token as a route parameter)
  • addExtraRequestDataSet can be used to test more requests on the same route (eg. test a secured route as both logged in and anonymous user). Returns RequestDataSet that you can use the same way as the result from changeDefaultRequestDataSet. All configured options will extend the values from default request data set (even when you change the default RequestDataSet after you add the extra RequestDataSet).

Note: All three methods of RouteConfigCustomizer accept string $debugNote as an argument. It is useful for describing the reasons of your configuration change because it may help you with debugging when the test fails.

Additionally you can override these methods in your implementation of HttpSmokeTestCase to further change the test behavior:

  • setUp to change the way your kernel is booted (eg. boot it with different options).
  • getRouterAdapter to change the object responsible for collecting routes from your application and generating urls.
  • createRequest if you have specific needs about the way Request is created from RequestDataSet.
  • handleRequest to customize handling Request in your application (eg. you can wrap it in database transaction to roll it back into original state).

Annotations

To make smoke test configuration a little easier, you can use the annotations:

DataSet

Used for setting expected status code based on provided paramteters.

@DataSet(statusCode=404, parameters={
    @Parameter(name="name", value="Batman")
})
  • arguments:
    • parameters (optional)
    • statusCode (optional, default = 200)

Parameter

Parameter defines value for specified parameter.

@Parameter(name="name", value="Batman")
  • arguments:
    • name (required)
    • value (required)

Skipped

Mark test as skipped

@Skipped()

You can add them directly to your controller methods. See the example in Shopsys\HttpSmokeTesting\Test\TestController.

Note: You should avoid using annotations with configuring via changeDefaultRequestDataSet() on same route. It may result in unexpected behavior.

Troubleshooting

Tests do not fail on non-existing route

PHPUnit by default does not fail on warnings. Setting failOnWarning="true" in phpunit.xml fixes this problem.

Contributing

Thank you for your contributions to Shopsys HTTP Smoke Testing package. Together we are making Shopsys Framework better.

This repository is READ-ONLY. If you want to report issues and/or send pull requests, please use the main Shopsys repository.

Please, check our Contribution Guide before contributing.

Support

What to do when you are in troubles or need some help? The best way is to join our Slack.

If you want to report issues, please use the main Shopsys repository.

You might also like...
A simple HTTP server behaving as proxy between webhooks and Appwrite Functions.
A simple HTTP server behaving as proxy between webhooks and Appwrite Functions.

A simple HTTP server behaving as proxy between webhooks and Appwrite Functions, allowing for instance Stripe payments integration into Appwrite.

An alternative to run cron jobs that uses simple HTTP requests

SilverStripe Simple Jobs An alternative to run cron jobs that uses simple HTTP requests. This module require SilverStripe CronTask. Why? Configuring c

A simple package to send HTTP header responses ✨

PHP HTTP Response Simple PHP package to easily send the right HTTP header responses to the browser 🐘 Requirement PHP v8.0 or higher 🚀 Installation T

Integrate reCAPTCHA using async HTTP/2, making your app fast with a few lines.
Integrate reCAPTCHA using async HTTP/2, making your app fast with a few lines.

ReCaptcha Integrate reCAPTCHA using async HTTP/2, making your app fast with a few lines. use Illuminate\Support\Facades\Route; Route::post('login', f

Enraged Xenomorph - DDD/CQRS Symfony Application Boilerplate
Enraged Xenomorph - DDD/CQRS Symfony Application Boilerplate

Enraged Xenomorph - DDD/CQRS Symfony Application Boilerplate This project is very opinionated attempt to compile a bit of experience, few good practic

Minimalist PHP frame for Core-Library, for Developing PHP application that gives you the full control of your application.

LazyPHP lightweight Pre-Made Frame for Core-library Install Run the below command in your terminal $ composer create-project ryzen/lazyphp my-first-pr

Simple php-imap integration for Symfony 2.8, 3.x and 4.x.

PHP-IMAP integration bundle Simple php-imap integration for Symfony 4.x, 5.x and 6.x. The version 1.5 and above are only compatible with Symfony 4+. P

Simple yet powerful, PSR-compliant, Symfony-driven PHP Blog engine.
Simple yet powerful, PSR-compliant, Symfony-driven PHP Blog engine.

brodaty-blog ✒️ Simple Blog Engine based on pure Markdown files. 💕 Works without database, caches HTML templates from Markdown files. 🚀 Fast and ext

Implement a
Implement a "Where's Wally" (or Waldo/Charlie) Captcha in a simple Symfony Project

Where's Wally ? Nowadays, we are often confronted with "Captcha". These tests to know if we are robots or not. They are all very boring and not very f

Comments
  • Allow Symfony 4, travis improvements

    Allow Symfony 4, travis improvements

    Description, reason for the PR:

    • having composer.lock in library is meaningless, it also makes pull requests harder due to possible conflicts (see https://getcomposer.org/doc/02-libraries.md#lock-file ) and the tests will not run against latest releases of depending libraries
    • symfony 4 is already in beta, would be nice to allow its installation
    • added build on PHP 7.1 and also lowest dependencies build - see https://evertpot.com/testing-composer-prefer-lowest/
    • php-cs-fixer changes behavior of self_ancestor check, thus the "self" in codestyle

    New feature: No BC breaks: No Standards and tests pass: We will see Licence: MIT

    opened by OndraM 3
  • Allow phpunit 6

    Allow phpunit 6

    Description, reason for the PR: Currently it does not work with phpunit 6, so this should help.

    New feature: No

    BC breaks: No

    Fixes issues: Not compatible with phpunit 6

    Standards and tests pass: Yes

    Licence: MIT

    opened by kunicmarko20 2
Releases(v1.1.0)
Owner
Shopsys
Shopsys
Dockerise Symfony Application (Symfony 6 + Clean Architecture+ DDD+ CQRS + Docker + Xdebug + PHPUnit + Doctrine ORM + JWT Auth + Static analysis)

Symfony Dockerise Symfony Application Install Docker Install Docker Compose Docker PHP & Nginx Create Symfony Application Debugging Install Xdebug Con

null 48 Jan 5, 2023
Integrate your PHP application with your HTTP caching proxy

FOSHttpCache Introduction This library integrates your PHP applications with HTTP caching proxies such as Varnish. Use this library to send invalidati

FriendsOfSymfony 338 Dec 8, 2022
Simple Symfony API-Platform Template which you can use to start to develop with symfony and api-platform

symfony-api-platform-skeleton Simple Template for Symfony API You can fork it and change the git remote to your Repo git remote set-url <your-git-remo

null 1 Jan 23, 2022
Testing your OpenApi documentation and your code easily.

Raven - How to test your API documentation and behavior. This library was written to allow testing OpenAPI documentation easily. It also allows verify

CH Studio 30 Dec 6, 2022
Simple Symfony currency exchange demo application (CLI)

Symfony currency exchange demo Keynotes Using a small Symfony installation as possible Using SQLite database for simplicity but with price of some cav

Vladimir Martsul 9 Oct 21, 2022
Allows Admins to quickly login as any user in the system during development/testing

SilverStripe User Switcher The module adds a small form both in the frontend to quickly login as any user in the system. The intended use is in testin

LiveSource 15 Mar 1, 2021
A Laravel test build by Incline Start-up Agency. Testing Git and framework functions.

Incognito-Confessions A laravel test build by Incline Start-up Agency. Testing Git and framework functions. Description A laravel starter for future t

null 6 Nov 9, 2022
Opens an interactive PHP console for running and testing PHP code.

wp-cli/shell-command Opens an interactive PHP console for running and testing PHP code. Quick links: Using | Installing | Contributing | Support Using

WP-CLI 20 Nov 4, 2022
PSpec is a testing framework for PHP, influenced by RSpec and jest. 🧪 This repo is a MIRROR of the GitLab source repository.

PSpec PSpec is a testing framework for PHP, influenced by RSpec and jest. This project is experimental and still needs a lot of work. Example // src/C

CodingPaws 0 Mar 31, 2022
Testing utilities for the psr/log package that backs the PSR-3 specification.

FIG - Log Test Testing utilities for the psr/log package that backs the PSR-3 specification. Psr\Log\Test\LoggerInterfaceTest provides a base test cla

PHP-FIG 3 Nov 19, 2022