4

我正在尝试进行单元测试,但我还需要找到一块拼图。

我要做的是为以下代码编写测试。在这种情况下,我有一个非常简单的前端控制器(用 PHP 编写)。

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

      $oController = new $sClassname();

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

      return $oResponse;
   }

}

我遇到的问题是因为代码创建了新对象。我可以轻松地模拟请求对象,以便我可以严格控制它在我的测试用例中实际执行的操作。我不确定用测试替身实际替换控制器的最佳方法。

IBM 的这篇文章建议使用工厂方法来创建我的控制器,然后用用于测试的特定类覆盖它:

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();
   }

}

然后进行测试可能是这样的:

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

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

(注意这不是文章所说的,但我认为如果这样做对我最有用)

另一种解决方案可能是拥有另一个创建控制器的类。这将是 frontController 的依赖类。这样,我可以在测试期间用测试替身替换工厂/创建类。像这样的东西:

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();
   }
}

我想依赖注入可以在前端控制器构造函数中处理,或者通过设置器而不是实际“路由”方法的参数来处理。

我想我更喜欢选项2。

这两种方法中的任何一种都是测试这种东西的正确方法吗?

(也许“好方法”在这里会更好!)

对选项 1 与选项 2 的任何想法或建议表示赞赏,或者确实有任何替代方案。请记住 - 关键是关于如何测试一个对象,该对象本身会创建其他对象作为其执行的一部分。

谢谢!

4

4 回答 4

4

您可能会发现这篇文章很方便。

它讨论了如何将对象创建与应用程序的实际运行分开。

于 2009-11-15T07:57:54.160 回答
2

我通常发现工厂是用于这种情况的好东西。除了可交换性方面,这意味着正在创建的对象所需的附加参数、数据或依赖项可以由工厂存储,因此实际请求新对象的对象不必知道它们的任何信息。 ..

于 2009-12-03T05:21:02.690 回答
0

您不想使用真正的控制器而是模拟,对吗?

在我看来,实现这一点的最简单方法是将请求子类化,以便它返回 MockController 的名称。

于 2009-11-13T13:18:42.327 回答
0

我假设您已经仔细考虑了您的断言,以便定义您正在测试的目标。请记住,单元测试将测试您的方法的返回,在这种情况下,它是 $oResponse (无论这可能是什么)。因此,您的测试断言将基于此返回值。由于我不知道您的代码片段中的返回值是什么,因此我只能演示一个您可以完成的示例。

我会推荐PHPUnit用于您的测试,因为它似乎是 PHP 恕我直言的最完整的包(许多人是 SimpleTest 的粉丝,以及......对他们自己来说)。

它看起来像这样(请注意,为简洁起见,我省略了包含。阅读PHPUnit 文档以获取更多信息):

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");
      }

希望我没有完全错过你所说的目的。这个故事的寓意是你只能测试你的方法返回什么。如果要从方法测试对象实例化,请针对实例化后返回该对象的方法使用 instanceof PHP 函数。

于 2009-11-13T14:22:37.627 回答