Outils pour utilisateurs

Outils du site


pele_mele:stack_exchange:stack_overflow:28071372

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentesRévision précédente
pele_mele:stack_exchange:stack_overflow:28071372 [2024/11/23 04:02] – supprimée - modification externe (Date inconnue) 127.0.0.1pele_mele:stack_exchange:stack_overflow:28071372 [2024/11/23 04:02] (Version actuelle) – ↷ Nom de la page changé de pele_mele:stack_exchange:stack_overflow:stackoverflow-28071372 à pele_mele:stack_exchange:stack_overflow:28071372 alexis
Ligne 1: Ligne 1:
 +====== How to test a concrete method calling an abstract method with phpunit ======
 +
 +I have an abstract class with concrete method. So I want to test those concrete methods.
 +
 +Here is my abstract class:
 +
 +<code php>
 +abstract class File {
 +    private $debug_filename_pattern = 'DELETE_ME_%s.debug';
 +    private $filename;
 +    private $filepath;
 +
 +    abstract public function buildFilename();
 +
 +    public function __construct($debug = false) {
 +        $filename = $this->buildFilename();
 +        if ($debug) {
 +            $filename = sprintf($this->debug_filename_pattern, $filename);
 +        }
 +        $this->filename = $filename;
 +        $this->buildFilepath();
 +    }
 +
 +    private function buildFilepath() {
 +        $this->filepath = ini_get('upload_tmp_dir') . DIRECTORY_SEPARATOR . $this->filename;
 +    }
 +}
 +</code>
 +
 +I read the section on testing abstract classes in [[https://phpunit.de/manual/current/en/test-doubles.html#test-doubles.mocking-traits-and-abstract-classes|phpunit documentation]] and I came up with that test:
 +
 +<code php>
 +final class FileTest extends \PHPUnit_Framework_TestCase {
 +    public function test() {
 +        $stub = $this->getMockForAbstractClass('MyBundle\File', [true]);
 +        $stub->expects($this->atLeastOnce())
 +                ->method('buildFilename')
 +                ->withAnyParameters()
 +                ->will($this->returnValue('test.log'));
 +        $this->assertEquals('C:\xampp\tmp\DELETE_ME_test.log.debug', $stub->getFilePath());
 +    }
 +}
 +</code>
 +
 +But it is not working. My assert always returns that it fails with this error message:
 +
 +<code diff>
 +Failed asserting that two strings are equal.
 +--- Expected
 ++++ Actual
 +@@ @@
 +-'C:\xampp\tmp\DELETE_ME_test.log.debug'
 ++'C:\xampp\tmp\DELETE_ME_.debug'
 +</code>
 +
 +I understand that my mock object is instantiated and then I add a mock for ''buildFilename'' method. Making my test always fail.
 +
 +Is there a way to mock my abstract method before instantiation? Should I refactor my abstract class instead?
 +
 +<WRAP help>
 +I don't think that you are able to set up your mock the way that you want. The construct method is being called when you ''->getMock()''. Then you are trying to set expectations after the fact.
 +
 +Generally, I find that when something becomes difficult to test as in this case it is a sign that there is an issue with the design. I think that the issue you have is that you are doing too much in your constructor in this case.
 +
 +You are doing all sorts of heavy lifting to determine the filepath on the construct of your object. Why not change it so that it happens when you call ''getFilePath''. Your class would end up looking like this:
 +
 +<code php>
 +abstract class File {
 +    private $debug_filename_pattern = 'DELETE_ME_%s.debug';
 +    private $filename;
 +    private $filepath;
 +    protected $debug;
 +
 +    abstract public function buildFilename();
 +
 +    public function __construct($debug = false) {
 +        $this->debug = $debug;
 +    }
 +
 +    private function buildFilepath() {
 +        $filename = $this->buildFilename();
 +        if ($this->debug) {
 +            $filename = sprintf($this->debug_filename_pattern, $filename);
 +        }
 +        $this->filename = $filename;
 +        $this->filepath = ini_get('upload_tmp_dir') . DIRECTORY_SEPARATOR . $this->filename;
 +    }
 +
 +    public function getFilePath() {
 +        if(!this->filepath) {
 +            $this->buildFilepath();
 +        }
 +
 +        return $this->filepath;
 +    }
 +}
 +</code>
 +
 +Now in your test to make sure that the path gets built only once just add your assertion one more time.
 +
 +<code php>
 +final class FileTest extends \PHPUnit_Framework_TestCase {
 +    public function test() {
 +        $stub = $this->getMockForAbstractClass('MyBundle\File', [true]);
 +        $stub->expects($this->once())
 +                ->method('buildFilename')
 +                ->withAnyParameters()
 +                ->will($this->returnValue('test.log'));
 +        $this->assertEquals('C:\xampp\tmp\DELETE_ME_test.log.debug', $stub->getFilePath());
 +        $this->assertEquals('C:\xampp\tmp\DELETE_ME_test.log.debug', $stub->getFilePath());
 +    }
 +}
 +</code>
 +</WRAP>
 +<WRAP info>
 +[[https://stackoverflow.com/questions/28071372/how-to-test-a-concrete-method-calling-an-abstract-method-with-phpunit|php - How to test a concrete method calling an abstract method with phpunit - Stack Overflow]]
 +</WRAP>