Api: Unit Testing: The version given was unknown or has no registered routes.

Created on 5 Jul 2016  路  22Comments  路  Source: dingo/api

Hi! I built an API using dingo/api 0.10.0, Laravel 5.1 and lucadegasperi/oauth2-server-laravel": "^5.1".
All my routes work fine in Postman/Paw.

The problem appears when I try to test the API using PHPUnit.
This is part of my route-api.php file

<?php

$api = app('Dingo\Api\Routing\Router');

$api->version(['v1'], function ($api) {

$api->post('oauth/access_token', function () {
    return response(
        \LucaDegasperi\OAuth2Server\Facades\Authorizer::issueAccessToken()
    )->header('Content-Type', 'application/json');
});

$api->group(['middleware' => ['oauth', 'api.auth']], function ($api) {
    $api->post('/register', 'YPS\Http\Controllers\Api\UserController@register');
});

And this is my test file UserRegistrationTest.php

class UserRegistrationTest extends ApiTestCase
{

public function setUp()
{
    parent::setUp();
    parent::afterApplicationCreated();
}

public function testRegisterSuccess()
{
    $data = factory(YPS\User::class)->make()->toArray();
    $data['password'] = 'password123';

    $this->post('api/register', $data, $this->headers)
        ->seeStatusCode(201)
        ->seeJson([
            'email' => $data['email'],
            'first_name' => $data['first_name'],
            'last_name' => $data['last_name'],
        ]);
}

public function testRegisterMissingParams()
{
    $this->post('api/register', [], $this->headers, $this->headers, $this->headers)->seeStatusCode(422);
}
}

The ApiTestCase simply retrieves a token and sets the headers.

private function setHeaders()
{
    $this->headers = [
        'Accept' => 'application/vnd.yps.v1+json',
        'Authorization' => 'Bearer ' . $this->OAuthAccessToken,
    ];
}

Now, the weird part is that the first test testRegisterSuccess runs perfectly and returns the response I expect. But the second one testRegisterMissingParams, even though it's the same route, returns this,

array:2 [
      "message" => "The version given was unknown or has no registered routes."
      "status_code" => 400
 ] 

I tracked the error and it is in the Laravel adapter here:

public function dispatch(Request $request, $version)
    {
        // it seems that the second time around can't find any routes with the key 'v1'
        if (! isset($this->routes[$version])) {
            throw new UnknownVersionException;
        }

        $routes = $this->mergeExistingRoutes($this->routes[$version]);

        $this->router->setRoutes($routes);

        return $this->router->dispatch($request);
    }

And further more, if i run one test at a time (eg comment one out, run test and then comment the other and run test) i see the result expected in both tests. The problem is when i run multiple tests... I have not tried yet running multiple tests from multiple files...

Any thoughts on that?

Thank you!

bug discussion

Most helpful comment

I am just testing this in Laravel 5.4 and what @boyd91 mentions about the require_once is true, I had this

    protected function mapApiRoutes()
    {
        require_once base_path('routes/api.php');
    }

And when I ran tests the first is ok and the ones after that got a 400.
Then i changed the require_once for require and all the tests passed.

All 22 comments

For the purpose of analyzes, try to run phpunit with --filter testRegisterMissingParams UserRegistrationTest tests/UserRegistrationTest.php (1st parameter is method name, 2nd is class name and 3rd is file path).

See if you get the same result when calling just that one method.

phpunit --filter UserRegistrationTest::testRegisterWithMissingParams  tests/api
PHPUnit 5.4.6 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 643 ms, Memory: 45.75MB

OK (1 test, 1 assertion)

It works when I run only one test. I breaks when I run multiple tests. Even if the are in separate files.

Would you be willing to build a minimal viable project on GitHub where you write the most simple code possible and writes 2 tests that fail just like yours? I would enjoy investigating this.

If you do, I suggest you make about 3 commits:

  • Clean Laravel install;
  • Installation of dependency packages (Dingo API and other packages you may make usage);
  • Route; Validation and Test files. Don't forget to include your .env.testing or a working sample of .env that leads to the problem.

+1

The version of dingo being used is a little old, the newest version, outside of dev-master, is 1.0.0-beta3, have you experienced this issue on that version @catalinbuletin?

I am getting this bug too even with Laravel 5.3 and last dingo version.

If I run the tests with the filter flag everything works, it fails when I run multiple tests (same endpoint).

Edit:

I have this requet:

$request = $this->post('/auth/login', [
    'login'    => $this->user->email,
    'password' => $password ?: $this->password,
]);

return json_decode($request->response->content())->token;

First test with this call is successful, returns a token. Second call, in another test, on the endpoint, returns this error message: "message":"The version given was unknown or has no registered routes.".

If I run the failed test with flag filter (only this test), it passes.

@hskrasek I know it is a little bit old, but it is the latest version that works with Laravel 5.1. Unfortunatelly we can't update Laravel at the moment.

@jakubkratina I asked the same question on StackOverflow and I got an answer which I did not get to test. You might check it out, and let me know:
http://stackoverflow.com/a/38599735/1571060

@catalinbuletin I already tried it but did not work (there is a problem with autoloading application). How have you solved the problem?

@jakubkratina I did not solve it yet. Moved away from it a little bit, now I can't find the time to do it... When I find a solution, I'll let you know!

Thank you. This is kind of problem to me, I cannot make tests before refactoring and migrating Lumen 5.2 to Laravel 5.3 :) See you.

@hskrasek: I have pulled the last release of Dingo API, using Laravel 5.3 and I still have this issue.

Am getting 404 Not Found errors. Using Lumen 5.3

@catalinbuletin Probably caused by the fact that your routes are not in the default routes.php. See #1243.

Solution: Don't put your routes in a file other than routes.php. Not an ideal fix but it might not be related to dingo api.

@intositeme @boyd91 My RouteServiceProvider:

public function map()
    {
        $this->mapApiRoutes();
        $this->mapWebRoutes();
    }

protected function mapWebRoutes()
    {
        Route::group([
            'middleware' => 'web',
            'namespace'  => $this->namespace,
        ], function ($router) {
            foreach (File::allFiles(base_path('routes/web')) as $file) {
                require $file->getPathname();
            }
        });
    }

protected function mapApiRoutes()
    {
        $api = app(Router::class);

        $api->version('v1', [
            'middleware' => 'api',
            'namespace'  => $this->namespace . '\Api',
        ], function ($api) {
            foreach (File::allFiles(base_path('routes/api')) as $file) {
                require $file->getPathname();
            }
        });
    }

So I have in the routes directory web and api directories with the routes.

@body91 I do have the routes structured in multiple files and require them in the routes.php. I'll try that solution but it's weird nonetheless.

I didn't test this myself.. But are you maybe using require_once to include the other route files?

I know I did and just thought to myself that that would be an explanation for the first request being run successfully and the next one fail. Laravel's TestCase could be trying to include the file for the second time, but require_once is preventing that.

I encountered the similar condition. If an API endpoint is accessed over browser or normal HTTP client, it works well. However, if the same API endpoint is tested by PHPUnit, it doesn't work and responses that the endpoint is not found.

After tracing the code, the situation is occasioned by the use WithoutMiddleware; in a unit testing, because, DingoAPI uses \Dingo\Api\Http\Middleware\Request for routing the HTTP request (see the source). Someone also mentioned the WithoutMiddleware issue (see the issue #17 in dingo/api-docs).

If there is use WithoutMiddleware; in the testing, it results that no middleware is passed into the \Illuminate\Routing\Pipeline::through() (see the source for more detail). The \Dingo\Api\Http\Middleware\Request stores in the $middelware in \Illuminate\Foundation\Http\Kernel. And \Dingo\Api\Http\Middleware\Request helps send a HTTP request to \Dingo\Api\Routing\Router.

I have the same problem with laravel 5.3 and dingo 1.0.*. This work for me. http://stackoverflow.com/a/38599735/7334447

I am just testing this in Laravel 5.4 and what @boyd91 mentions about the require_once is true, I had this

    protected function mapApiRoutes()
    {
        require_once base_path('routes/api.php');
    }

And when I ran tests the first is ok and the ones after that got a 400.
Then i changed the require_once for require and all the tests passed.

Yes, you are right. I have tried that and it worked!

I'm having this same issue.
where should I add the code below?

    protected function mapApiRoutes()
    {
           require_once base_path('routes/api.php');
    }

Phew! Finally figured it out.
I am building a package with own routes.

Initially I was doing this in my package service provider include_once __DIR__ . '/V1/Http/routes.php';

changing it to include __DIR__ . '/V1/Http/routes.php'; resolved the issue.
Thanks for the head up

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cristiammercado picture cristiammercado  路  3Comments

sukh-gill picture sukh-gill  路  3Comments

HTMHell picture HTMHell  路  4Comments

lloricode picture lloricode  路  3Comments

Sogl picture Sogl  路  4Comments