Mockery Behat and Laravel

Posted: 2016-08-31 00:33:05

This is a quick view of using this library https://github.com/laracasts/Behat-Laravel-Extension and a Mockery Trait (should be included soon but for now put the below in your features/bootstrap folder and run composer dump).

The example here is a method that looks like this

features/bootstrap/ExampleContext.php

        $partial = Mockery::mock(\App\Broadcasts\ColumnsAndRowsDoneProcessingEmail::class, [$this->segment])->makePartial();

        $partial->shouldReceive('sendEmail')->never();

        $repo = App::make(\App\JobsRepository::class);

        $partial->handle($repo);

When this is done running I would get pass BUT if I change it never to once and run it again I still get green :(

But I need to include the Mockery. In my case I made this trait that will allow me to also tests and mock events.

<?php


trait MockeryTrait
{

    public $firedEvents = [];
    public $beforeApplicationDestroyedCallbacks = [];

    /**
     * @AfterScenario
     */
    public function reviewMocks()
    {
        if ($this->app) {
            foreach ($this->beforeApplicationDestroyedCallbacks as $callback) {
                call_user_func($callback);
            }

            $this->app->flush();

            $this->app = null;
        }
        if (class_exists('Mockery')) {
            Mockery::close();
        }

        $this->beforeApplicationDestroyedCallbacks = [];
    }

    protected function beforeApplicationDestroyed(callable $callback)
    {
        $this->beforeApplicationDestroyedCallbacks[] = $callback;
    }


    /**
     * Specify a list of events that should be fired for the given operation.
     *
     * These events will be mocked, so that handlers will not actually be executed.
     *
     * @param  array|string  $events
     * @return $this
     *
     * @throws \Exception
     */
    public function expectsEvents($events)
    {
        $events = is_array($events) ? $events : func_get_args();

        $this->withoutEvents();

        $this->beforeApplicationDestroyed(function () use ($events) {
            $fired = $this->getFiredEvents($events);

            if ($eventsNotFired = array_diff($events, $fired)) {
                throw new Exception(
                    'These expected events were not fired: ['.implode(', ', $eventsNotFired).']'
                );
            }
        });

        return $this;
    }

    /**
     * Mock the event dispatcher so all events are silenced and collected.
     *
     * @return $this
     */
    protected function withoutEvents()
    {
        $mock = Mockery::mock('Illuminate\Contracts\Events\Dispatcher');

        $mock->shouldReceive('fire')->andReturnUsing(function ($called) {
            $this->firedEvents[] = $called;
        });

        $this->app->instance('events', $mock);

        return $this;
    }

    /**
     * Filter the given events against the fired events.
     *
     * @param  array  $events
     * @return array
     */
    protected function getFiredEvents(array $events)
    {
        return $this->getDispatched($events, $this->firedEvents);
    }

    /**
     * Filter the given classes against an array of dispatched classes.
     *
     * @param  array  $classes
     * @param  array  $dispatched
     * @return array
     */
    protected function getDispatched(array $classes, array $dispatched)
    {
        return array_filter($classes, function ($class) use ($dispatched) {
            return $this->wasDispatched($class, $dispatched);
        });
    }

    /**
     * Check if the given class exists in an array of dispatched classes.
     *
     * @param  string  $needle
     * @param  array  $haystack
     * @return bool
     */
    protected function wasDispatched($needle, array $haystack)
    {
        foreach ($haystack as $dispatched) {
            if ((is_string($dispatched) && ($dispatched === $needle || is_subclass_of($dispatched, $needle))) ||
                $dispatched instanceof $needle) {
                return true;
            }
        }

        return false;
    }
}

This pulls in a trait Laravel uses in PHPUnit.

So now when I run that test I get red and when I set it back to zero I get green!