Mocking static methods with Mockery alais

43 Views Asked by At

I am well aware that static methods are the death of tesablity, however, I am dealing with a legacy codebase that is built around global scope and static everything. Mockery appears to provide a hack for mocking static methods that does work in some capacity.

I can create a simple test like this and everything works well:

public function testOne()
{
    $mockDB = Mockery::mock(DB::class);
    $mockDB
        ->shouldReceive('getRow')
        ->andReturn((object) ['Col1' => 'Val1']);
    
    $mockStatic = Mockery::mock('alias:MyGarbageClass')
        ->shouldReceive('DB')
        ->andReturn($mockDB);
    
    // Calls MyGarbageClass::DB()
    $subject = SubjectClass::myMethod();
    
    $this->assertEquals($subject, 'Val1');
}

The problem I am having is when I create a second test:

public function testTwo()
{
    $mockDB = Mockery::mock(DB::class);
    $mockDB
        ->shouldReceive('getRow')
        ->andReturn((object) ['Col1' => 'Val2']);
    
    $mockStatic = Mockery::mock('alias:MyGarbageClass')
        ->shouldReceive('DB')
        ->andReturn($mockDB);
    
    // Calls MyGarbageClass::DB()
    $subject = SubjectClass::myMethod();
    
    $this->assertEquals($subject, 'Val2');
}

I am obviously getting back 'Val1' instead of 'Val2' in testTwo() and that is somewhat expected. I am having a hard time figuring out how to work around that result. I played around with using processes isolation for each test using @runInSeparateProcess but that didn't seem to help which I found surprising. The only other thing I can think of is specifying the arguments that are passed to getRow but that is probably not going to be practical because the first argument is a SQL query that will be hard to match exactly and there will inevitably be overlap with other tests if I don't. I will also need to mock other static methods that call other classes static methods that will take no arguments and produce different results similar to what SubjectClass::myMethod() is doing in this case.

What are my options here? I thought I could use @runInSeparateProcess to get a fresh environment without the pre-existing autoloaded classes but that doesn't seem to be the case. I was also thinking about stubbing out a version of MyGarbageClass I can inject mocks into, but that doesn't solve the eventual need to mock SubjectClass::myMethod() for some other test. I still think some type of processes isolation is the path of least resistance but I am not sure how to accomplish that.

0

There are 0 best solutions below