7

I'm using mcamara/laravel-localization package and I can't figure out how to make it work with my unit tests. Both of the following fail with red:

// 1. This one results in "Redirecting to http://myapp.dev/en"
$this->get('/')->assertSee('My App Homepage');

// 2. This one results in 404
$this->get('/en')->assertSee('My App Homepage');

In the browser, http://myapp.dev returns 302 with a redirect to http://myapp.dev/en, fair enough. However, http://myapp.dev/en returns 200. So both cases work 100% fine on the front-end, but not with unit tests.

I do have some customization however, which once again, works like charm in the browser.

// in web.php
Route::group([
    'prefix' => app('PREFIX'), // instead of LaravelLocalization::setLocale()
    'middleware' => ['localeSessionRedirect', 'localizationRedirect']],
    function() {
        Route::get('/', function() {
            return view('home');
        });
    }
]);

// in AppServiceProvider.php
public function boot()
{
    // This, unlike LaravelLocalization::setLocale(), will determine the 
    // language based on URL, rather than cookie, session or other
    $prefix = request()->segment(1); // expects 'en' or 'fr'
    $this->app->singleton('PREFIX', function($app) use ($prefix) {
        return in_array($prefix, ['en', 'fr']) ? $prefix : null;
    });
}

Hopefully this code makes sense to you. Thanks!

UPDATE

I addressed this problem with the package in a GitHub issue #435.

UPDATE 2

Insofar as I could figure it out, it seems that you can safely test your localized routes as long as you specify the locale in the base URL in your phpunit XML file:

<env name="APP_URL" value="http://myapp.dev/en"/>

However, this would work for your localized GET endpoints (which start with a locale prefix, e.g. 'en'), but not for non-localized POST, PUT, etc. (which don't have any prefix). Hence, you can't really test both kinds of endpoints at the same time, unless you use Dusk (which I don't, as it's an overkill and much slower, almost the same as doing it manually).

Alex
  • 3,719
  • 7
  • 35
  • 57

2 Answers2

1

I found that if you dump the request URL during testing, it is always http://myapp.dev no matter what endpoint you're accessing. So both LaravelLocalization::setLocale() and my custom app('PREFIX') return null, meaning that not a single route is ever localized during testing. You are screwed either way because if you try to access a route without a locale prefix, you get a 302, but if you do specify the locale, the framework can't find a definition for that route.

One article helped me discover a temporary solution: you need to hideDefaultLocaleInURL to true in laravellocalization.php. This way, the routes matching your default locale won't have any prefix, so you can test them as if they were non-localized.

However, the problem still persists, because how are you supposed to test your application when it is localized? (For ex., when you have language-specific routes that need to be tested). This poses the question whether this package is even compatible with unit testing per se...

Alex
  • 3,719
  • 7
  • 35
  • 57
  • I have the same issue. If I set `hideDefaultLocaleInURL` to `true`, I have some more errors in my unittests. My solution was, to set `useAcceptLanguageHeader` to `false` and `hideDefaultLocaleInURL` to `true`. – poldixd Jan 23 '19 at 16:34
  • @poldixd if you want to set `useAcceptLanguageHeader` to true, you just have to set the session like this `$response = $this->withSession(['locale' => 'en']) ->get('/');` then it will work (ofc you still need `hideDefaultLocaleInURL`. – Adam Sep 12 '19 at 08:34
1

The problem

Using mcamara / laravel-localization when I test a show route I get a 404 error.

For instance, testing this route returns me a 404:

Route::get('/posts/{post:slug}', [PostController::class, 'show'])->name('posts.show');

The test:

/** @test */
public function itShouldDisplayThePostsShowViewToGuestUser()
{
    $response = $this->get("/posts/{$this->post1->slug}");

    $response->assertStatus(200);
    $response->assertViewIs('posts.show');
}

The solution

I solved hiding the locale from the URL while testing.

Creating this env variable at the end of phpunit.xml.

    ...
    <env name="LOCALIZATION_HIDE_DEFAULT_LOCALE" value="true"/>
    </php>
</phpunit>

And in config/laravellocalization.php setting hideDefaultLocaleInURL like this:

'hideDefaultLocaleInURL' => env('LOCALIZATION_HIDE_DEFAULT_LOCALE', false)

This solution was inspired by this this post:
https://github.com/mcamara/laravel-localization/issues/161#issuecomment-381367191

Davide Casiraghi
  • 15,591
  • 9
  • 34
  • 56