English 中文(简体)
unit test a method that creates an object
原标题:

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!

最佳回答

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...

问题回答

You might find this article handy.

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

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.

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.





相关问题
Template Classes in C++ ... a required skill set?

I m new to C++ and am wondering how much time I should invest in learning how to implement template classes. Are they widely used in industry, or is this something I should move through quickly?

JSON with classes?

Is there a standardized way to store classes in JSON, and then converting them back into classes again from a string? For example, I might have an array of objects of type Questions. I d like to ...

Object-Oriented Perl constructor syntax and named parameters

I m a little confused about what is going on in Perl constructors. I found these two examples perldoc perlbot. package Foo; #In Perl, the constructor is just a subroutine called new. sub new { #I ...

Passing another class amongst instances

I was wondering what is the best practice re. passing (another class) amongst two instances of the same class (lets call this Primary ). So, essentially in the constructor for the first, i can ...

Where can I find object-oriented Perl tutorials? [closed]

A Google search yields a number of results - but which ones are the best? The Perl site appears to contain two - perlboot and perltoot. I m reading these now, but what else is out there? Note: I ve ...

热门标签