0
\$\begingroup\$

Calculator class:

class Calculator
{
    /**
     * @return array
     */
    function doPowTable ($n)
    {
        $table = array();
        for ($i = 1; $i <= $n; $i++)
        {
            $table[] = array(
                'i' => $i,
                'factor' => pow($i, 2)
            );
        }
        return $table;
    }
}

Formatter class:

class Formatter
{
    /**
     * @return array
     */
    function getAndFormat ($n)
    {
        $table = (new Calculator()).doPowTable ($n);
        return array_map (function($item) {
            $item['i_factor'] = $item['i'].'_'.$item['factor'];
            return $item;
        }, $table);
    }
}

They say it's bad because the helper hard-codes the Calculator. But I think it's how it is supposed to be. It would have been better if I didn't give the $n to it but an array created by the doPowTable() method. Why?

\$\endgroup\$

migrated from stackoverflow.com Jul 27 '15 at 13:21

This question came from our site for professional and enthusiast programmers.

  • 7
    \$\begingroup\$ Problem #1 .... $table = (new Calculator()).doPowTable ($n); the . in PHP is a concatenation operator; do you mean $table = (new Calculator())->doPowTable ($n); \$\endgroup\$ – Mark Baker Jul 27 '15 at 10:51
4
\$\begingroup\$

What if I want to put the Formatter class under test? How can I mock the Calculator dependency if it is hard-coded?

I suggest you put that into a separate public method so that it would be possible for other developers to create a partial mock and test it. It will also be clearer since you start reading the class what its dependencies are.

class Formatter
{
    /**
     * @var Calculator
     */ 
    private $calculator;

    /**
     * @param Calculator $calculator
     * @return $this     
     */
    public function setCalculator(Calculator $calculator)
    {
        $this->calculator = $calculator;

        return $this;
    }

    /**
     * @return Calculator
     */
    public function getCalculator()
    {
        return $this->calculator;
    }

    /**
     * @return array
     */
    function getAndFormat ($n)
    {
        $table = $this->getCalculator()->doPowTable($n);

        return array_map (function($item) {
            $item['i_factor'] = $item['i'].'_'.$item['factor'];
            return $item;
        }, $table);
    }
}

This way you can either provide a different calculator or a mocked one for testing purposes.

Please consider the example below. It shows how to create a partial mock of the Formatter class to override the Calculator dependency.

$mockedCalculator = $this->getMock(Calculator::class);

// set your mocked calculator behaviour here

$formatter = $this->getMockBuilder(Formatter::class)
    ->disableOriginalConstructor()
    ->setMethods(['getCalculator'])
    ->getMock();

$formatter
    ->expects($this->once())
    ->method('getCalculator')
    ->willReturn($mockedCalculator);

$n = 123; // it's just an example
$this->assertEquals($expectedValue, $formatter->getAndFormat($n));
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.