Slim Framework endpoint unit testing

爱⌒轻易说出口 提交于 2019-11-27 16:28:50

问题


I'm trying to write some PHPUnit tests for my small slim framework app, but don't see anywhere in the docs that point to a way to do a full request and assert on the response (either containing text or a 200 status, or anything, really).

Is there any way to do this that anyone has found/used?


回答1:


Here is example how you may test your Slim application:

https://github.com/mac2000/SlimTestable

Suppose we have simple application:

<?php
use Slim\Slim;

require_once 'vendor/autoload.php';

$app = new Slim();

$app->get('/', function(){
    echo 'home';
})->name('home');

$app->get('/hello/:name', function($name){
    echo "hello $name";
})->name('hello');

$app->map('/login', function() use($app) {
    if($app->request()->params('login')) {
        $app->flash('success', 'Successfully logged in');
        $app->redirect($app->urlFor('hello', array('name' => $app->request()->params('login'))));
    } else {
        $app->flash('error', 'Wrong login');
        $app->redirect($app->urlFor('home'));
    }
})->via('GET', 'POST');

$app->run();

How do we test it?

Create App class:

<?php // src/App.php
use Slim\Slim;

class App extends Slim {
    function __construct(array $userSettings = array())
    {
        parent::__construct($userSettings);

        $this->get('/', function(){
            echo 'home';
        })->name('home');

        $this->get('/hello/:name', function($name){
            echo "hello $name";
        })->name('hello');

        $this->map('/login', function() {
            if($this->request()->params('login')) {
                $this->flash('success', 'Successfully logged in');
                $this->redirect($this->urlFor('hello', array('name' => $this->request()->params('login'))));
            } else {
                $this->flash('error', 'Wrong login');
                $this->redirect($this->urlFor('home'));
            }
        })->via('GET', 'POST');
    }

    /**
     * @return \Slim\Http\Response
     */
    public function invoke() {
        $this->middleware[0]->call();
        $this->response()->finalize();
        return $this->response();
    }
}

Notice that we move all our routes to new class constructor, also notice new invoke method, which do the same as run method except it returns response rather than echoing it out.

Now your index.php file might be like this one:

<?php
require_once 'vendor/autoload.php';

$app = new App();
$app->run();

And now it is time for tests:

<?php // tests/ExampleTest.php
use Slim\Environment;

class ExampleTest extends PHPUnit_Framework_TestCase {
    private $app;

    public function setUp()
    {
        $_SESSION = array();
        $this->app = new App();
    }

    public function testHome() {
        Environment::mock(array(
            'PATH_INFO' => '/'
        ));
        $response = $this->app->invoke();

        $this->assertContains('home', $response->getBody());
    }

    public function testHello() {
        Environment::mock(array(
            'PATH_INFO' => '/hello/world'
        ));
        $response = $this->app->invoke();

        $this->assertTrue($response->isOk());
        $this->assertContains('hello world', $response->getBody());
    }

    public function testNotFound() {
        Environment::mock(array(
            'PATH_INFO' => '/not-exists'
        ));
        $response = $this->app->invoke();

        $this->assertTrue($response->isNotFound());
    }

    public function testLogin() {
        Environment::mock(array(
            'PATH_INFO' => '/login'
        ));
        $response = $this->app->invoke();

        $this->assertTrue($response->isRedirect());
        $this->assertEquals('Wrong login', $_SESSION['slim.flash']['error']);
        $this->assertEquals('/', $response->headers()->get('Location'));
    }

    public function testPostLogin() {
        Environment::mock(array(
            'REQUEST_METHOD' => 'POST',
            'PATH_INFO' => '/login',
            'slim.input' => 'login=world'
        ));
        $response = $this->app->invoke();

        $this->assertTrue($response->isRedirect());
        $this->assertEquals('Successfully logged in', $_SESSION['slim.flash']['success']);
        $this->assertEquals('/hello/world', $response->headers()->get('Location'));
    }

    public function testGetLogin() {
        Environment::mock(array(
            'PATH_INFO' => '/login',
            'QUERY_STRING' => 'login=world'
        ));
        $response = $this->app->invoke();

        $this->assertTrue($response->isRedirect());
        $this->assertEquals('Successfully logged in', $_SESSION['slim.flash']['success']);
        $this->assertEquals('/hello/world', $response->headers()->get('Location'));
    }
}

You should notice few things:

While setting up test we are creating $_SESSION array for test purposes and instantiate our App class object.

In tests rather than run we are calling invoke which do the same, but returns response object.

Environment::mock used to mock requests which are processed with our application.




回答2:


Ok, so I was able to rough it and make it work. Here's an example of an endpoint test class.

Assuming you're working in a development environment, you can execute curl requests to your own localhost, thus testing before committing to a repo.

First, create your class:

class ApiEndpointsTest extends PHPUnit_Framework_TestCase
{
    protected $api_url = "http://localhost/api/v1";

    //create a function that will allow you to call API endpoints at-will.
    private function loadEndpoint($url) {
        $ch = curl_init(); 
        curl_setopt($ch, CURLOPT_URL, $url); 
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($ch);
        $info = curl_getinfo($ch);
        curl_close($ch);
        return array(
          'body' => $output,
          'info' => $info
        );
    }

    //this allows you to write messages in the test output
    private function printToConsole($statement) {
        fwrite(STDOUT, $statement."\n");
    }

Using this, you can write a test function for a particular endpoint response:

//this will test the actual body of the response against something expected.
public function testGetUserResponse() {
  $this->printToConsole(__METHOD__);
  $url = $this->api_url."/users/124";
  $response = $this->loadEndpoint($url);
  $expected = '[{"name":"John Smith","email":"john@acme.com"}]';
  $this->assertEquals($response['body'], $expected);
}

In a separate test, you can test any other property of the API call's response:

public function testGetUserMimeType() {
  $this->printToConsole(__METHOD__);
  $url = $this->api_url."/users/124";
  $response = $this->loadEndpoint($url);
  $this->assertEquals($response['info']['content_type'], 'application/json');
}

Your info property options can be found here: http://php.net/manual/en/function.curl-getinfo.php

Side note: if anyone reading this is an expert at PHPUnit and knows a better way, I'm interested in learning about it -- I'm new to PHPUnit.



来源:https://stackoverflow.com/questions/17840928/slim-framework-endpoint-unit-testing

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!