Catfish
Catfish

Reputation: 19294

Mockery TypeError when mocking class

I'm trying to create a mock object and pass it in to a constructor. I'm getting the error

Users            
 ✘ GetUser ItShouldThrowAnExceptionIfUserDoesNotExist
   ┐
   ├ Failed asserting that exception of type "TypeError" matches expected exception "Foo\Exceptions\UserNotFoundException". Message was: "Argument 2 passed to Foo\Clients\Users::__construct() must be an instance of Foo\Api\UsersApi, instance of Mockery_0__UsersApi given, called in /Users/tomcaflisch/myproject/tests/Clients/UsersTest.php on line 25" at
   ├ /Users/tomcaflisch/myproject/lib/Clients/Users.php:26                                                                                                                                                                                                                                                                                                                                     
   ├ /Users/tomcaflisch/myproject/tests/Clients/UsersTest.php:25                                                                                                                                                                                                                                                                                                                               
   ├ .                                                                                                                                                                                                                                                                                                                                                                                                                 
   ┴

According to the docs this should work.

Users class

class Users
{
    public function __construct(?string $secret, UsersApi $usersApi = null)
    {
    }

UsersTest class

use Mockery\Adapter\Phpunit\MockeryTestCase;

class UsersTest extends MockeryTestCase
{
    public function testGetUser_ItShouldThrowAnExceptionIfUserDoesNotExist()
    {
        $this->expectException(UserNotFoundException::class);
        $this->expectExceptionMessage("User with id, email, or username [email protected] not found");

        $usersApi = Mockery::mock('UsersApi');
        $usersApi->shouldReceive('get')
            ->andThrow(new UserNotFoundException("User with id, email, or username [email protected] not found"));

        $usersClient = new Users(null, $usersApi);
    }

Upvotes: 2

Views: 2388

Answers (2)

s3c
s3c

Reputation: 1853

I usually go this route:

<?php

namespace Tests\Unit;

use App\Services\ServiceToBeMocked;
use App\Services\ServiceToBeTested;
use App\Jobs\JobToBeTested;
use Mockery;
use Mockery\MockInterface;
use Tests\TestCase;

class ServiceToBeTestedTest extends TestCase
{
    private ServiceToBeTested $testService;

    public function testSomePartThatDoesntUseTheInjectedDependency(): void
    {
        /** @var ServiceToBeMocked $mockService */
        $mockService = Mockery::mock(ServiceToBeMocked::class);

        // now you can use it
        $this->testService = new ServiceToBeTested($mockService);

        // continue with testing

        // assert things happened
    }

    public function testSomePartThatUsesTheInjectedDependency(): void
    {
        // The part you're testing actually uses the mocked service
        // by calling its method mockedMethod, which should in this
        // given test return 'a-string-value'

        /** @var ServiceToBeMocked $mockService */
        $mockService = Mockery::mock(ServiceToBeMocked::class, function (MockInterface $mock) {
            $mock->shouldReceive('mockedMethod')
                ->andReturn('a-string-value');
        });

        // now you can use it
        $this->testService = new ServiceToBeTested($mockService);

        // continue with testing

        // assert things happened
    }

    public function testSomethingThatUsesServiceToBeMockedInAJob(): void
    {
        // In this case we don't need to assign it to a variable
        Mockery::mock(ServiceToBeMocked::class, function (MockInterface $mock) {
            $mock->shouldReceive('mockedMethod')
                ->andReturn('a-string-value');
        });

        // JobToBeTested::handle(ServiceToBeMocked $mockService)
        app()->call(JobToBeTested::class . '@handle);

        // assert things happened
    }
}

Upvotes: 1

Catfish
Catfish

Reputation: 19294

Ah I just figured it out. I need to use the full namespace.

i.e.

replace

$usersApi = Mockery::mock('UsersApi');

with

$usersApi = Mockery::mock('Foo\Api\UsersApi');

Upvotes: 3

Related Questions