0

To test below method

public function handle($loginDetails): User
{
    $authUser = $this->user->where('user_id', $loginDetails->id)->first();
    if (!$authUser) {
        $authUser = $this->user->create([
            'name' => $loginDetails->name,
            'email' => $loginDetails->email,
            'profile_image' => $loginDetails->avatar_original,
            'user_id' => $loginDetails->id
        ]);
    }

    return $authUser;
}

I am mocking and testing it as below

public function shouldHandleNewUser()
{
        $mockBuilder = $this->getMockBuilder(Builder::class)
            ->disableOriginalConstructor()
            ->setMethods(['first'])
            ->getMock();
        $mockBuilder->method('first')
            ->willReturn(null);

        $mockUser = $this->getMockBuilder(User::class)
            ->disableOriginalConstructor()
            ->setMethods(['where', 'create'])
            ->getMock();

        $mockUser->method('where')
            ->willReturn($mockBuilder);

        $mockUser->method('create')
            ->willReturn(new User());

        $loginDetails = new class {
            public $id = 1;
            public $name = 'testUser';
            public $email = 'test@test.com';
            public $avatar_original = 'http://someurl.com/image.jpg';
        };

        $registrationHandler = new RegistrationHandler($mockUser);
        $this->assertInstanceOf(User::class, $registrationHandler->handle($loginDetails));
}

When it reaches to the create method it is throwing

PHPUnit_Framework_MockObject_BadMethodCallException

What I am doing wrong here ?

Raheel
  • 8,716
  • 9
  • 60
  • 102

1 Answers1

1

You can't mock static methods with PHPUnit (I'm assuming User::create() is static).

Limitation: final, private, and static methods

Please note that final, private, protected, and static methods cannot be stubbed or mocked. They are ignored by PHPUnit's test double functionality and retain their original behavior.

There used to be a staticExpects() method to work around it. It was deprecated in PHPUnit 3.8 and removed in PHPUnit 3.9. As Sebastian Bergmann says, the solution is to not use static methods (and I agree with him)

ishegg
  • 9,685
  • 3
  • 16
  • 31
  • You are correct, It happens to be a static method in Laravel Eloquent. Any work around ? – Raheel Feb 12 '18 at 17:28
  • The problem with Eloquent models is that they're actually implementations of an Active Record pattern, which means they tightly couple persistence and business logic, and they're full of static methods, which make the classes really hard to test. If you're able to use `Mockery` (I think it comes with Laravel), take a look at [this](https://stackoverflow.com/questions/37456518/how-to-mock-static-methods-of-an-eloquent-model) question. – ishegg Feb 12 '18 at 19:03
  • Thanks for explanation. I changed my code from using ```static create``` method to a instance method in Eloquent ```save```. I kinda feel bad by changing code to satisfy the test. But your explanation cleared my concept. thanks – Raheel Feb 13 '18 at 13:35
  • 1
    Raheel, I think refactoring is a big part of coding, so don't feel bad! That said, there's a *lot* of information on unit testing Eloquent Models (I've seen people just do integration testing on them instead of unit testing), so definitely try to find some articles, maybe you can keep your code! Here are some: [first](https://laracasts.com/discuss/channels/testing/testing-eloquent-models), [second](https://code.tutsplus.com/tutorials/testing-like-a-boss-in-laravel-models--net-30087). Good luck! – ishegg Feb 13 '18 at 13:42