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.