2016-07-22 74 views
0

我正在运行CakePHP 2.8.X,并且正在尝试为模型函数编写单元测试。模拟CakePHP中模型方法中的方法

我们打电话给模型Item,我试图测试它的getStatus方法。

但是,该模型在getStatus方法内调用其find

因此,像这样:

class Item extends Model 
{ 
    public function getStatus($id) { 
     // Calls our `$this->Item-find` method 
     $item = $this->find('first', [ 
     'fields' => ['status'], 
     'conditions' => ['Item.id' => $id] 
     ]); 

     $status = $item['status']; 

     $new_status = null; 

     // Some logic below sets `$new_status` based on `$status` 
     // ... 

     return $new_status; 
    } 
} 

逻辑设置“$new_status”是一个有点复杂,这就是为什么我想写一些测试它。

但是,我不完全确定如何覆盖Item::getStatus内的find呼叫。

通常当我需要模拟模型的功能,我使用$this->getMock加上method('find')->will($this->returnValue($val_here)),但我并不想完全模仿我Item因为我想测试其实际getStatus功能。

也就是说,在我的测试功能,我将被调用:

// This doesn't work since `$this->Item->getStatus` calls out to 
// `$this->Item->find`, which my test suite doesn't know how to compute. 
$returned_status = $this->Item->getStatus($id); 
$this->assertEquals($expected_status, $returned_status); 

那么,如何沟通,我真正Item模型我的测试中,它应该覆盖其内部调用其find方法?

回答

1

我知道这必须是他人所面临的问题模型的独立,它原来的PHPUnit有一个非常简单的方法来解决这个问题!

This tutorial本质上给了我答案。

我确实需要创建一个模拟,但只有在'find'传球,因为我想嘲笑的方法,PHPUnit的帮忙,留下所有其他方法在我的模型独自覆盖它们。

相关部分从上面的教程是:

传递方法的名称数组您getMock第二个参数产生,其中的方法,你已经确定

  • 是否所有的存根模仿对象,
  • 在默认情况下都返回NULL,
  • 很容易克服的

尽管方法,你也没查出

  • 是否所有的嘲笑,
  • 运行包含名为重点煤矿)当方法中的实际代码,
  • 不要让你覆盖返回值

含义,我可以把th在嘲笑模型,并直接从我的打电话给我getStatus方法。该方法将运行其真实的代码,并且当它到达find()时,它只会返回我传入$this->returnValue的任何内容。

我使用dataProvider来传递我想要find方法返回的结果,以及在我的assertEquals调用中测试的结果。

所以我的测试功能看起来像:

/** 
* @dataProvider provideGetItemStatus 
*/ 
public function testGetItemStatus($item, $status_to_test) { 
    // Only mock the `find` method, leave all other methods as is 
    $item_model = $this->getMock('Item', ['find']); 

    // Override our `find` method (should only be called once) 
    $item_model 
     ->expects($this->once()) 
     ->method('find') 
     ->will($this->returnValue($item)); 

    // Call `getStatus` from our mocked model. 
    // 
    // The key part here is I am only mocking the `find` method, 
    // so when I call `$item_model->getStatus` it is actually 
    // going to run the real `getStatus` code. The only method 
    // that will return an overridden value is `find`. 
    // 
    // NOTE: the param for `getStatus` doesn't matter since I only use it in my `find` call, which I'm overriding 
    $result = $item_model->getStatus('dummy_id'); 

    $this->assertEquals($status_to_test, $result); 
} 

public function provideGetItemStatus() { 
    return [ 
     [ 
      // $item 
      ['Item' => ['id' = 1, 'status' => 1, /* etc. */]], 

      // status_to_test 
      1 
     ], 

     // etc... 
    ]; 
} 
+1

您可以接受你自己的答案让其他人知道你已经解决您的问题。干得好,这对其他人很有用。 – vascowhite

0

模拟查找的一种方法可能是使用测试特定的子类。

你可以创建一个TestItem来扩展item和覆盖find,所以它不会执行db调用。

另一种方式可以是封装NEW_STATUS逻辑和单元测试它