Refactoring General Tests to a Basic Test

Is it good to reorganize a test case if two or more test classes that test different implementations of the same interface / abstract class have common tests , but with different devices?

Let's say that the code and tests look like this:

interface MathOperation
{
    public function doMath($a, $b);
}

class Sumator implements MathOperation
{
    public function doMath($a, $b)
    {
        return $a + $b;
    }
}


class Multiplicator implements MathOperation
{
    public function doMath($a, $b)
    {
        return $a * $b;
    }
}

// tests
class SumatorTest extends PHPUnit_Framework_TestCase
{
    /**
     * @var Sumator
     */
    protected $sumator;

    public function setUp()
    {
        $this->sumator = new Sumator;
    }

    /**
     * @dataProvider fixtures
     */
    public function testDoMath($a, $b, $expected)
    {
        $result = $this->sumator->doMath($a, $b);
        $this->assertEqual($expected, $result);
    }

    public function fixtures()
    {
        return array(
            array(1, 1, 2);
            array(2, 1, 3);
            array(100, -1, 99);
        );
    }
}

class MultiplicatorTest extends PHPUnit_Framework_TestCase
{
    /**
     * @var Multiplicator
     */
    protected $multiplicator;

    public function setUp()
    {
        $this->multiplicator = new Multiplicator;
    }

    /**
     * @dataProvider fixtures
     */
    public function testDoMath($a, $b, $expected)
    {
        $result = $this->multiplicator->doMath($a, $b);
        $this->assertEqual($expected, $result);
    }

    public function fixtures()
    {
        return array(
            array(1, 1, 1);
            array(2, 1, 2);
            array(100, -1, -100);
        );
    }
}

and I want them (tests) to look like this:

class MathOperationTestCase extends PHPUnit_Framework_TestCase
{
    /**
     * @var MathOperation
     */
    protected $operation;

    public function setUp()
    {
        $this->operation = $this->createImpl();
    }

    /**
     * @return MathOperation
     */
    abstract function createImpl();

    /**
     * @dataProvider fixtures
     */
    public function testDoMath($a, $b, $expected)
    {
        $result = $this->operation->doMath($a, $b);
        $this->assertEqual($expected, $result);
    }

    abstract public function fixtures();
}

class SumatorTest extends MathOperationTestCase
{
    public function createImpl()
    {
        return new Sumator;
    }

    public function fixtures()
    {
        return array(
            array(1, 1, 2);
            array(2, 1, 3);
            array(100, -1, 99);
        );
    }
}

class MultiplicatorTest extends MathOperationTestCase
{
    public function createImpl()
    {
        return new Multiplicator;
    }

    public function fixtures()
    {
        return array(
            array(1, 1, 1);
            array(2, 1, 2);
            array(100, -1, -100);
        );
    }
}

This seems more structured, but may not have readability . Therefore, in the end, I'm not sure if this is a practical practice.

+3
source share
4 answers

After some consideration, I came to the conclusion that only restraining this approach reduces code duplication.

, - . Multiplicator, .

class Multiplicator implements MathOperation
{
    private $factor; // added factor which influences result of doMath()

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

    public function doMath($a, $b)
    {
        return ($a * $b) * $factor;
    }
}

, Sumator Multiplicator , Multiplicator , .

class MultiplicatorTest extends MathOperationTestCase
{
    // rest of code

    public function testDoMath2($ab, $b, $factor, $expected)
    {
        $multiplicator = new Multiplicator($factor);
        $result = $multiplicator->doMath($a, $b);
        $this->assertEqual($expected, $result);
    }
}

, no-no...

class Multiplicator implements MathOperation
{
    // rest of code

    public function __construct($factor = 1) // default value set in class
    {
        $this->factor = $factor;
    }
}

... modyfing. , , - .

class MultiplicatorTest extends MathOperationTestCase
{
    // rest of code

    public function createImpl()
    {
        return new Multiplicator(1); // added default value
    }
}

, , .

.

+1

, . , , . , , .

+1

PHPUnitTest, ! . , , Sumator Multiplicator - , , , .

, , ( ), - , , .

, , " " . , , , . , , , .

+1

, :

  • If the base class contains only general utilities / helper methods / classes for the application you are working on, i.e. mock class creators.
  • If the product under test shares some code with other products, but somewhat extends to it; this way you mirror this in the base class of the test and its children.
0
source

All Articles