ideas icon indicating copy to clipboard operation
ideas copied to clipboard

[Bug] mix() function throw exceptions during testing.

Open mathieutu opened this issue 7 years ago • 24 comments

Hi, as @themsaid asked in https://github.com/laravel/framework/issues/19552, I'm reposting here :

I have a problem with the mix() function. When I'm executing my tests, especially with travis I don't run all the webpack stuff: I don't need it, as I'm just testing controllers, services, etc.

Not Js and css things.

BUT, when I'm trying to make a get on a route which return a view, I always have a an exception from the mix() helper method.

Example :

Route::get(/foo, function () { return view('bar'); }

$response = $this->get('/foo');
$response->assertViewIs('bar');

Will throw a The Mix manifest does not exist. exception.

This is really annoying to have to install modules and compile assets we don't need..!

And we can't overwrite, or mock, or anything, because this is not part of the container or something like that, but just a basic function in helpers.php file.

Maybe we can make a service for that, and just do:

function mix($path, $manifestDirectory = '')
{
    return app('mix')->mix($path, $manifestDirectory);
}

in helpers.php

It will permit to mock it in tests:

app()->bind('mix', function () {
    return Mockery::mock(Mix::class)->shouldReceive('mix')->andReturn('')->getMock();
});

or something like that.

I can make the PR when we have choose a solution.

For now, I've just made this bad thing to fix it:

// New function which is called instead of mix in my views...
function mix_except_in_tests($path, $manifestDirectory = '') {
    if  (app()->runningUnitTests()){
        return '';
    }

    return mix($path, $manifestDirectory);
}

This is also a (quickest and dirtiest) option, but it may cause conflict with Laravel dusk ?

We also could do sort of:

function mix($path, $manifestDirectory = '') {
    if  (app()->runningUnitTests() && config('should_not_compile_during_testing')) {
        return '';
    }

    ... // old mix function.
}

I'm waiting for all your good ideas guys !

Matt'

mathieutu avatar Jun 12 '17 20:06 mathieutu

Just ran into this issue on CircleCI.

joshmanders avatar Jun 12 '17 21:06 joshmanders

A while ago Taylor said he was interested in resolving this issue, so I think a PR would definitely be welcome.

I think I'd rather keep the mix function and not introduce another one.

Maybe a config value like mix.environments?

sebastiandedeyne avatar Jun 13 '17 08:06 sebastiandedeyne

function mix($path, $manifestDirectory = '')
{
    return app('mix')->mix($path, $manifestDirectory);
}

This is the way to go in my opinion.

JulienTant avatar Jun 13 '17 08:06 JulienTant

Sounds good for me @sebastiandedeyne!

So for you we have to let all this stuff just in the helper function? I think it could be a good idea to migrate that to a proper dedicated service.

edit: didn't see @JulienTant comment (and your +1 on it)

mathieutu avatar Jun 13 '17 08:06 mathieutu

Changed my mind, actually prefer @JulienTant's idea 😄

sebastiandedeyne avatar Jun 13 '17 08:06 sebastiandedeyne

So, first case I described? Just a container, and then we have to mock it?

Or we can mix both: migrate to a proper service, AND use a config variable, which is my opinion.

mathieutu avatar Jun 13 '17 09:06 mathieutu

Yeah that would definitely be easier to use. Not sure about what the variable should be called though...

Something like require_in_environments is way longer that Laravel config keys generally are...

sebastiandedeyne avatar Jun 13 '17 09:06 sebastiandedeyne

Yep, but I like mix.environments. It may oblige to have a file just for that, but why not?!

or maybe in view.php, so config('view.mix_environments') ?

I personally prefer the first case.

mathieutu avatar Jun 13 '17 09:06 mathieutu

There's two cases where the mix() function is useful :

  • You want to use HMR
  • You want to use the manifest file

I can understand that you want to mock it for the tests when you don't want to run your assets compilation, but i'm not a supporter of having a configuration option to "bypass" the function when you decide to use it (instead of just calling the assets() helper).

If you have this special case when you want to change something or bypass for some reason, you can always overload mix in a ServiceProvider to use your own implementation.

JulienTant avatar Jun 13 '17 09:06 JulienTant

Using a manifest file and not wanting to depend on assets in a CI environment seems like a common enough use case to have an "easy way out" imo, instead of having to maintain a custom service provider in your application.

sebastiandedeyne avatar Jun 13 '17 09:06 sebastiandedeyne

You can simply mock the app('mix') in your tests then.

I'm quite against the configuration here because you add in the code complexity which has the only purpose of helping you doing tests. Using app('mix') helps you decouple and allows you to overload / mock. Using a 'switch' variable feels a bit crappy to me IF the only reason is tests.

JulienTant avatar Jun 13 '17 11:06 JulienTant

@JulienTant As you can see in my post, it was my first idea. But in fine, if we are a lot to have this need, it can be cool to do something easier.

If it's not a config variable, it totally can be just method which mock mix(), like Mix::mock() or something like that.

The only problem here is dusk, so the real question is:

Is there a reason why for the same test we want or not compile assets depending of the environment?

I don't think so personally, so this solution is totally ok for me.

mathieutu avatar Jun 13 '17 12:06 mathieutu

Instead of resolving stuff out of the container, why not just check to see if mix-manifest.json exists, if it does cache it and return from that, otherwise just return the value requested.

joshmanders avatar Jun 13 '17 14:06 joshmanders

@joshmanders because in case of a non testing env, mix() should trow an error if the manifest doesn't exist. The is the case now and it should do that actually.

mathieutu avatar Jun 13 '17 14:06 mathieutu

@sebastiandedeyne @JulienTant Here we are 😃 ! I'm waiting for your reviews!

mathieutu avatar Jun 16 '17 17:06 mathieutu

Guys, I'm sorry but @taylorotwell closed all the PRs I've made for that, without any consideration for all the comments, reviews, and after 3 weeks of discussions.

I've no idea of what is the problem, code is cleaner, fully tested, and all the demanded features was added.

I'm done with that, I've lost enough time, if someone want to try, please do !

The last PR is here : https://github.com/laravel/framework/pull/19895

mathieutu avatar Jul 18 '17 12:07 mathieutu

Thanks for your work @mathieutu and please don't get discouraged, as @taylorotwell mentioned on https://github.com/laravel/framework/pull/19895#issuecomment-313861655 smaller improvements are always better than rewriting the whole thing in one go.

While i would really appreciate your PR to be merged i also see @taylorotwell point of view.

Maybe @taylorotwell can shed some light on what he would prefer to see done first?

brunogaspar avatar Jul 18 '17 14:07 brunogaspar

Just ran into the same issue, thanks for your work @mathieutu! Using your helper function for the time being, hope @taylorotwell will fix this.

royduin avatar Sep 20 '17 10:09 royduin

Yeah, thank you @mathieutu for working so hard on a fix for this, really disappointed this is still an issue.

ghost avatar Oct 26 '17 13:10 ghost

Just an observation: I too am having this issue. I did use the method that @mathieutu suggested: function mix($path, $manifestDirectory = '') { return app('mix')->mix($path, $manifestDirectory); } And although it works great with PHPUnit, it is causing my Dusk tests to fail.

amylashley avatar Dec 04 '17 21:12 amylashley

@amylashley Can you give us some more information ? What did you do exactly, and what is the error ?

mathieutu avatar Dec 05 '17 09:12 mathieutu

Sure @mathieutu. I used this package to change the loading order so that I could add a helpers.php file. Then in my helpers.php file I added this method:

function mix($path, $manifestDirectory = '') {
    if  (app()->runningUnitTests()){
        return'';
    }

    return mix($path, $manifestDirectory);
}

This works great with PHPUnit. The test that was failing is now passing. However, with the same code in place, running Dusk (or Selenium with this package) both fail and give me only a white browser screen. That's the only feedback I seem to be able to get from Dusk- just a white screen. Once I comment out my new mix method, the Dusk tests are back to passing.

amylashley avatar Dec 05 '17 15:12 amylashley

Hi, it's totally normal that in that case dusk fail, your function can't work properly. The mix() function is overwritten, so you can't call it inside itself. You're just doing some recursive calls...

mathieutu avatar Dec 05 '17 16:12 mathieutu

This should be closed. You can simply run $this->withoutMix() in your test setup now. See https://github.com/laravel/framework/pull/30900

miken32 avatar Jun 04 '21 17:06 miken32