yayuj's avatar

Testing a method that calls other method

I'm wondering, how do I test a method that calls other method? Is it better to expect a certain result from the other method or expect that the method is going to call the other method?

I'm in this situation: I have an abstract class, and that class has two public method, and one private, the two public methods calls the private one. I can mock the abstract class and pass it to the invoke method of the reflection, but if I try to expect a method of the mock to be called, it doesn't work.

abstract class Foo
{
  public function A()
  {
    $this->C('foo');
  }
  public function B()
  {
    $this->C('bar');
  }

  private function C($arg)
  {
    //...
  }
}

// test
$foo = $this->getMockForAbstractClass(Foo::class);
$method = new ReflectionMethod(Foo::class, 'C');
$method->setAccessible(true);
$foo->expects($this->once())->method('C')->with('foo');
$method->invoke($foo, 'foo');

It doesn't work. - Is it possible? What is the best choice?

0 likes
3 replies
JeffreyWay's avatar

I think you're complicating this way, way too much. If you're tying to use reflection for a test, it most likely means you're doing it wrong.

It doesn't matter if the method calls another method; you're still writing tests against the output. So it makes no difference.

yayuj's avatar

"It doesn't matter if the method calls another method; you're still writing tests against the output. So it makes no difference."

@JeffreyWay - What you mean with that?

This is just an example of code that I'm writing. This is actually an abstract factory pattern where the abstract factory has a private method and two other public. The factories that inherits from the abstract factory will only pass the arrays that the factory can create using the constructor. Example:

abstract class WidgetFactory {

 private $button = [];
private $panels = [];

function __construct(array $button, array $panels)
{
    $this->button = $button;
    $this->panels = $panels;
}

public function makeButton($name)
    {
  return $this->makeWidget($this->button, $name);
  }

 public function makePanels($name)
  {
   return $this->makeWidget($this->panels, $name);
 }

  private function makeWidget(array $widgets, $name)
  {
    if (! isset($widgets[$name]))
    {
      throw new InvalidArgumentException('It wasn\'t possible to make the widget.');
    }

    return new $widgets[$name];
  }
}

class QtFactory extends WidgetFactory
{
  private $button = [
    'button' => QtButton::class
  ];

  private $panels = [
    'panels' => QtPanels::class
  ];

  function __construct()
  {
    parent::__construct($this->button, $this->panels);
  }

}

It is really hard to find implementations of design patterns with examples of tests.

bobbybouwmann's avatar

What Jeffrey means is that if you have a function MyController::getAll() for example then you expect an array of items. It doens't matter what the getAll() method is doing, since you just expect an array of items.

So if the getAll() method is calling the get() function if a for loop for example then you test that separately from the getAll() function. The get() function should return one object of an item.

It's not the best example but you get the idea ;)

class MyController extends Controller {
    
    public function getAll()
    {
        $ids = [1, 3, 4, 8];
        $objects = [];
        
        foreach($ids as $id)
        {
            $objects[] = MyObject::find($id);
        }

        return $objects;
    }

    public function get()
    {
        return MyObject::first();
    }

}
class TestMyController extends TestCase {

    public function testGetAll()
    {
        // test if it returns an array
    }

    public function testGet()
    {
        // test if it returns a single object
    }

}
1 like

Please or to participate in this conversation.