views:

150

answers:

4

I'm trying to get my head round Unit Testing and there's one more piece of the jigsaw I need to find.

What I'm trying to do is write tests for the following code. In this case, I've got a really simple Front Controller (written in PHP).

class frontController
{
   public function routeRequest($oRequest)
   {
      $sClassname = $oRequest->getController();
      $sMethod = $oRequest->getAction();

      $oController = new $sClassname();

      $oResponse = $oController->{$sMethod}($oRequest);

      return $oResponse;
   }

}

The problem I have is because the code creates new objects. I can easily mock the request object so that I can tightly control what it will actually do within my test case. I'm not sure the best way to actually replace the controller with a test double.

This article from IBM suggests having a factory method for creating my controller and then overriding this with a specific class used for testing:

class frontController
{
   public function routeRequest($oRequest)
   {
      $sMethod = $oRequest->getAction();

      $oController = $this->createController($oRequest);
      $oResponse = $oController->{$sMethod}($oRequest);

      return $oResponse;
   }

   protected function createController($oRequest)
   {
      $sClassname = $oRequest->getController();
      return new $sClassname();
   }

}

and then for testing perhaps something like this:

class testFrontController extends frontController
{
   public function setMockController($oMockController)
   {
      $this->oMc = $oMockController;
   }

   protected function createController($oRequest)
   {
      return $this->oMockController;
   }
}

(note this isn't quite what the article says, but I'm thinking it would be most useful to me if it did this)

Another solution could be to have another class that creates the controller. This would then be a dependent class of the frontController. This way I can replace the factory/creation class during testing with a test double. Something like this:

class frontController
{
   public function routeRequest($oRequest, $oControllerFactory)
   {
      $sMethod = $oRequest->getAction();

      $oController = $oControllerFactory->create($oRequest);
      $oResponse = $oController->{$sMethod}($oRequest);

      return $oResponse;
   }
}

class controllerFactory
{
   public function create($oRequest)
   {
      $sClassname = $oRequest->getController();
      return new $sClassname();
   }
}

I guess the dependency injection could be taken care of in the front controller constructor or via a setter instead of a parameter to the actual "route" method.

I think I prefer option 2.

Is either of these two methods the right way of going about testing this kind of thing?

(perhaps "good way" would be better word here!)

Any thoughts or suggestions on option 1 vs option 2 appreciated or indeed any alternatives. Remember - the key thing is about how to test an object that itself creates other objects as part of its execution.

Thanks!

A: 

You do not want to use the real controller but a mock, right ?

It seems to me the simplest way to achieve this would be to subclass the request so that it returns the name of a MockController.

philippe
A: 

I assume you have thought through your assertions so as to define the goal of what exactly you are testing. Keep in mind that unit tests are going to be testing the returns from your methods, which, in this case, is $oResponse (whatever this may be). As a result, your test assertions will be based on this return value. Since I don't know what that return value is from your code snippets, I can only demonstrate an example that you can complete.

I would recommend PHPUnit for your testing as it seems to be the most complete package for PHP imho (many are fans of SimpleTest, as well ... to each their own).

It would look something like this (Please note that I have left out includes for brevity. Read the PHPUnit documentation for more information):

class AimTest extends PHPUnit_Framework_TestCase{
      private $_controller = null;
      private $_request = null;

      public function setUp(){
             $this->_controller = new frontController();
             //what does this object's type?
             $this->_request = new requestObject();  
      }

      public function testObjectCreation(){
            /*
             * note, that this is only one of several assertions that could
             * be made depending on the return value
             */
             $return = $this->_controller->routeRequest($this->_request);
             //tailor to what you expect your output to be
             $this->assertTrue($return == "my expected output");
      }

Hope I didn't miss the mark completely on your stated purpose. Moral of the story is that you can only test what your methods return. If you want to test object instantiation from a method, use the instanceof PHP function against a method that returns that object after instantiation.

Paul Shoemaker
I'm not sure I did think through exactly what I wanted to test before I set out on this! Good advice! Roughly speaking this is exactly what I want. However your example would also inadvertantly test the other object(s) are working as well (eg: the controller). I guess I'm trying to isolate just this ONE class, and I understood that to do this one should use test doubles (mocks/stubs). Very handy answer none-the-less.
Dave
+1  A: 

You might find this article handy.

It discusses how object creation should be separated from the actual running of the application.

WW
I like it. One of the "missing concepts" often seems to be the difference between object building/configuration and runtime.
kyoryu
A: 

I generally find factories to be a good thing to use for this scenario. In addition to the swappability aspect, it means that additional parameters, data, or dependencies required by the object being created can be stored by the factory, and so the object which actually requests the new object doesn't have to know anything about them...

kyoryu
I think you're right. Factories are the way to go; they definitely help with dependencies (like you've said). Sometimes I think you end up with too many factories, but overall I'm happy to have that downside.
Dave
At some point you just break down and use a DI framework :)
kyoryu