1

I created a wrapper function in a helper file to wrap a globally used method, like getTimestamp(). The helper file is put in the same namespace as the file I am testing ('The model file'), a namespace like 'Project\Models\TeamName'. The hypothetical model file uses getTimestamp() function and does calculations to check from birth year. I want to test edge cases in the calculations so I overrode the 'getTimestamp()' function to always return 125 in the helper file.

However, this is causing other phpunit tests to fail that use getTimpestamp(). How can I tear it down so the 'require_once' with my helper file is undone, so the rest of the phpunit tests pass? The phpunit test class and SUT are in far away namespaces.

Right now I have a PHPUnit class (located in Project\Testing\PHPUnit\Models\TeamName)

namespace Project\Testing\PHPUnit\Models\TeamName;
require_once '/testing/phpunit/models/teamname/testHelper.php';

use Project\Models\TeamName\MyModel

class MyModelTest {
    const correctAge = 75; 

    public function testAge(){
        $model = new MyModel(); 
        $result = $model -> calculateAgeFromBirthYear(50);
        assertEquals(self::correctAge, $result); 
    }
}

And the helper file (located in Project\Testing\PHPUnit\Models\TeamName)

namespace Project\Models\TeamName; 
function getTimestamp(){
    //today is year 125
    return 125; 
}

And the SUT/model (located Project\Models\TeamName)

namespace Project\Models\TeamName; 
class MyModel {
    function calculateAgeFromBirthYear($birthYear){
        $date = new DateTime();
        $today = $date->getTimestamp(); 
        return $today - $birthYear;
    }
}

I don't want other phpunit classes to inherit a getTimestamp() that always returns 125, I want to undo the requires_once

greg b
  • 27
  • 6
  • Well, guess it is impossible XD – greg b Jul 22 '19 at 10:57
  • These two URLs seem closer to what I'm trying to achieve: https://ericdraken.com/phpunit-mock-hard-dependencies-aliases/ https://phpunit.readthedocs.io/en/7.3/annotations.html#preserveglobalstate – greg b Jul 22 '19 at 19:28
  • I put my own answer since this is what I did, feel free to respond with a better answer I will change it :) – greg b Jul 24 '19 at 15:15

1 Answers1

0

So this is what worked in my case, not necessarily every case.

In the MyModel class, I put a function called "getTimestampWrapper", which then called "getTimestamp" and does nothing else. I have my calculateAgeFromBirthYear function look like this now:

namespace Project\Models\TeamName; 
class MyModel {
    function calculateAgeFromBirthYear($birthYear){
        $today = getTimestampWrapper(); 
        return $today - $birthYear;
    }
    function getTimestampWrapper(){
        $date = new DateTime();
        $todayWrapper = $date->getTimestamp(); 
        return $todayWrapper;
    }
}

In MyModelTest, I mocked the MyModel object, then used onConsecutiveCalls with to expect the results properly.

  //make sure a function called 'getTimestampWrapper' is in your model
  $model = $this->getMockBuilder(MyModel::class)
      ->setMethods('getTimestampWrapper')
      ->getMock();

  //my onConsecutiveCalls will get me the fake timestamps I want
  $model  ->method('getTimestampWrapper')
      ->will(
          $this->onConsecutiveCalls(...[125, 125, 100])
      );
  //run your assertEquals now

So now when I call $model->calculateAgeFromBirthYear(50) in my unit tests, it will call use 125, 125, 100 for the timestamps

greg b
  • 27
  • 6