ideas icon indicating copy to clipboard operation
ideas copied to clipboard

Add ability to unfake facade

Open idsulik opened this issue 5 years ago • 11 comments

It will be great to be able to unfake the facade: Bus::unfake();

If I call Bus::fake() , then there is no proper way to revert this, I have to make new Dispatcher and swap the fake with original.

idsulik avatar Apr 01 '20 07:04 idsulik

If you need to fake only a specific subset you can bass an array to fake method in order to let you use others jobs.

Also you can split your test into multiple tiny tests instead of relying on a big one with one Bus::fake()

Never had the need to unfake, maybe it's a code smell of a big test that can be refactored

ludo237 avatar Apr 01 '20 14:04 ludo237

@ludo237 , for example I have several tests and more of them create model via factory and there is an observer which observe the model and make some operations(run jobs and etc.), so I need ability to fake the job in all tests except one where I will test the job after model was created via rest.

idsulik avatar Apr 02 '20 07:04 idsulik

You should create your mock without the observer in place if you don't need that in a specific test

User::withoutEvents(function() {
return factory(User::class)->create(); // Or whatever you use to mock 
});

This way you create your model via factory and then you can test your jobs on your REST Http test

Checkout the API here

ludo237 avatar Apr 02 '20 07:04 ludo237

I even added a helper in my test suite for this:

    /**
     * Saves the given models without any events
     *
     * The event disable/enable logic is from
     * https://laravel.io/forum/09-14-2014-turn-off-eloquent-events?page=1#reply-29610
     *
     * @param Model $model
     * @return void
     */
    protected function modelSaveWithoutEvents(Model $model): void
    {
        $model::withoutEvents(function () use ($model) {
            $model->save();
        });
    }

mfn avatar Apr 02 '20 07:04 mfn

@ludo237 I need observer creating method, not the saved method. Anyway, I need fake the job, not the events.

I'll try to explain - There is a job and observer which call the job and I need to fake the job in all tests except one test. I call Bus::fake(MyJobClass); and in my test I want unfake(or remove the job from fake list) to have ability to test the job(no unit test, I need test it via rest)

idsulik avatar Apr 02 '20 07:04 idsulik

I need to fake the job in all tests except one test.

If you have different tests you can call Bus::fake(MyJobClass::class) in every tests except the one when you need it.

Basically if you have a setUp method inside your test with Bus::fake() then move it down to every method where you need to fake it except the one where you don't need it

ludo237 avatar Apr 02 '20 08:04 ludo237

@ludo237 Yes, I can do it, but it will be elegant to have ability to unfake job for the test.

idsulik avatar Apr 02 '20 10:04 idsulik

@idsulik mixed feeling about it. I'd like to see each test in isolation from other tests. That's why I try to avoid using setUp and tearDown when not necessary.

I'd rather create a separate test or a class of tests specifically for mixing different domains if needed

ludo237 avatar Apr 02 '20 10:04 ludo237

@ludo237 , for example, I have several tests that use User model, each of the tests need it, so I prefer create that user in setUp method of the base test class.
The user model has events that run job, I need fake the job but in one test I want to have ability to run job and test it

idsulik avatar Apr 02 '20 11:04 idsulik

Here's an example of how to make this work.

In your setUp() do this...

$this->realQueue = Queue::getFacadeRoot();
Queue::fake();

Then later in a test you can do this...

Queue::swap($this->realQueue);

I hope this helps someone.

rmcdaniel avatar Mar 02 '21 04:03 rmcdaniel

Thanks for your example @rmcdaniel!

In my case I had to re-enable Eloquent model events so in my setUp() I used:

$this->realEvent = Event::getFacadeRoot();
Event::fake();

And later:

// use Illuminate\Database\Eloquent\Model;
Model::setEventDispatcher($this->realEvent);

clementmas avatar Jun 25 '21 12:06 clementmas