jamesfairhurst's avatar

Mocking a Class persists over tests

Hello,

I'm using L4 and just started to use Mockery in a test class, after that class finishes the next test class is throwing a Mockery fetchMock() error. It seems the Mock persists over the different test classes. I've tried to manually refresh the app but still having the issue.

The mock is an alias because the it's send method is static

$this->mockMailHelper = $this->mock('alias:MailHelper');

I'm closing properly in the tearDown method:

Mockery::close();

When I run the test classes in isolation they both pass, however together they fail. I've inspected the class in the ioc in the test class that doesn't contain the Mock and it's getting the Mocked class from the previous test file.

dd($this->app->make('MailHelper'));

Hope that all makes sense! Anyone have any ideas?

0 likes
5 replies
bobbybouwmann's avatar

You always need to tell Mockery to close the Mock.

class MyTest extends TestCase {

    public function tearDown()
    {
        \Mockery::close();
    }

    // Your tests

}
jamesfairhurst's avatar

@bobbybouwmann thanks for the reply, yes I'm already doing that. It's still in the ioc in the other Test class that doesn't contain any Mockery logic.

bobbybouwmann's avatar

Can you past your complete test class? You might missed something we find ;)

jamesfairhurst's avatar

Sure, first class:

<?php

use Symfony\Component\DomCrawler\Crawler;

class AdminCustomerVoucherTest extends TestCase
{
    public function setUp()
    {
        parent::setUp();

        $this->resetEvents(array('Customer','User','Customer_Address','Customer_Voucher'));

        $this->seed('AdminUserSeeder');
        $this->seed('DeliverySeeder');
        $this->seed('CustomerSeeder');
        $this->seed('AdminChannelSeeder');

        $this->be(User::find(1));

        $this->mockMailHelper = $this->mock('alias:MailHelper');
    }

    public function tearDown()
    {
        Mockery::close();

        DB::table('admin_users')->truncate();
        DB::table('admin_roles')->truncate();
        DB::table('customers')->truncate();
        DB::table('customer_addresses')->truncate();
        DB::table('customer_vouchers')->truncate();
        DB::table('delivery_depots')->truncate();
        DB::table('delivery_rounds')->truncate();
        DB::table('delivery_postcodes')->truncate();
        DB::table('channels')->truncate();

        parent::tearDown();
    }

    public function mock($class)
    {
        $mock = Mockery::mock($class);

        $this->app->instance($class, $mock);

        return $mock;
    }

    public function testCreateWithVoucher()
    {
        $voucherData = array(
            'customer_id' => Customer::first()->id,
            'channel_id' => Channel::first()->id,
            'amount' => 10,
        );

        $this->mockMailHelper->shouldReceive('send')->once();

        $response = $this->call('POST', 'admin/customer-voucher/create', $voucherData);

        $this->assertRedirectedTo('admin/customer/edit/' . $voucherData['customer_id']);

        $this->assertEquals(1, Customer::first()->vouchers()->count());

        $voucher = Customer::first()->vouchers()->first();

        $this->assertEquals(10, $voucher->amount);
        $this->assertEquals(10, $voucher->remaining);
        $this->assertEquals(null, $voucher->conditional_spend_amount);
        $this->assertEquals(null, $voucher->expiry_date);
    }

}

second class to test Mock:

<?php

class AdminDayMoveTest extends TestCase
{
    public function setUp()
    {
        parent::setUp();

        // $mailHelper = new MailHelper;
        // $this->app->instance('MailHelper', $mailHelper);
        dd($this->app->make('MailHelper'));
    }

    public function tearDown()
    {
        parent::tearDown();
    }

}
KingMob's avatar

The issue is Mockery intercepts the loading of the class when you use alias: and that applies for the remainder of the PHP session's life. Calling Mocker::close() doesn't change this.

Instead, add the following before each class that uses alias mocks. It tells PHPUnit to run in a separate PHP process, so it won't affect other tests.

/**
 * @runTestsInSeparateProcesses
 * @preserveGlobalState disabled
 */
1 like

Please or to participate in this conversation.