0

I have a situation like the example classes below where I have a child class that overrides a parent method. In certain situations it will return the original result from the parent class.

How do I go about unit testing this in Mockery?

I was going to create a partial and then define the result from the parent call (shouldRecieve('getName')->andReturn('parent')), but obviously that then mocks out the method I want to test.

Is there a way to mock the result of the parent implementation of the method?

class Parent {
    public function getName() {
        /*** Lots of other complicated calls to other 
             services that I don't want to mock. 
        ***/

        return 'Parent';
    }
}

class Child {
    public function getName(bool $someParameter) {
        if ($someParameter) {
            return 'child';
        }

        return parent::getName();
    }

1 Answer 1

0

So, there is a possible way, but let me first clarify some stuff.

You are calling parent::getName(), on a class that is extending nothing. I will assume you just forgot to write extends Parent after class Child.

Assuming the previous stuff, I will write your code again:

class Parent
{
    public function getName()
    {
        return 'Parent';
    }
}

class Child extends Parent
{
    public function getName(bool $someParameter)
    {
        return $someParameter ? 'child' : parent::getName();
    }
}

To be able to mock this, you have to write this:

$mock = \Mockery::mock(Child::class);

$mock->shouldReceive('getName')
    ->andReturn('Parent');

// Then do whatever you want to but you will end up calling $mock->getName()

As you said, you are not mocking the Parent class, so you are like "ignoring" the implementation of Chield's getName() method. As you said, that is not exactly good in your case, as you want to test exactly that code, and by mocking it, you are not testing the code but testing that Mockery does what it should do, mock that method and return Parent when you call getName() no matter what the parameter is.

Another solution is to use dependency injection, you can modify the Child class (if your project allows you to) to receive the Parent class like this:

class Parent
{
    public function getName()
    {
        return 'Parent';
    }
}

class Child
{
    private Parent $parent;

    public function __construct(Parent $parent)
    {
        $this->parent = $parent;
    }

    public function getName(bool $someParameter)
    {
        return $someParameter ? 'child' : $this->parent->getName();
    }
}

Now the test would be like this:

$mock = \Mockery::mock(Parent::class);

$mock->shouldReceive('getName')
    ->andReturn('Parent');

$child = new Child($mock);

// Then do whatever you want to but you will end up calling $child->getName()

But this may not help a lot if you want to overwrite stuff, because the baseline Parent has logic that works. So maybe passing it as a dependecy makes no sense and you must extend it.

In that case, you should have something different in Parent, like this:

class AnotherObjectWithLogic
{
    public function doSomething(): string
    {
        return 'Parent';
    }
}

class Parent
{
    private AnotherObjectWithLogic $logicObject;

    public function __construct(AnotherObjectWithLogic $logicObject)
    {
        $this->object = $logicObject;
    }

    public function getName(): string
    {
        return $this->object->doSomething;
    }
}

class Child extends Parent
{
    public function getName(bool $someParameter): string
    {
        return $someParameter ? 'child' : parent::getName();
    }
}

Now, your test would look like this:

$mock = \Mockery::mock(AnotherObjectWithLogic::class);

$mock->shouldReceive('doSomething')
    ->andReturn('Parent');

$child = new Child($mock);

// Then do whatever you want to but you will end up calling $child->getName()

This way you can have both classes as pristine as possible, but you only need to pass the object or whatever you are calling to do something when you need to call getName on Parent class.

There is also dependency inversion but it may not be exactly what you are aiming for, nor your issue. But still helps.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.