Php 如何对Laravel中调用另一个模型的方法进行单元测试
我是测试和编写可测试代码的新手,我想了解一下处理这个简单场景的正确方法。我读过其他类似标题的问题和答案,但它们似乎没有对我的问题给出明确的答案 我有一个控制器,它在我的Php 如何对Laravel中调用另一个模型的方法进行单元测试,php,laravel,unit-testing,phpunit,Php,Laravel,Unit Testing,Phpunit,我是测试和编写可测试代码的新手,我想了解一下处理这个简单场景的正确方法。我读过其他类似标题的问题和答案,但它们似乎没有对我的问题给出明确的答案 我有一个控制器,它在我的Picking类的实例上调用shipped()方法: class MyController extends \BaseController { public function controllerMethod() { $picking = new Picking; $picking->
Picking
类的实例上调用shipped()
方法:
class MyController extends \BaseController {
public function controllerMethod() {
$picking = new Picking;
$picking->shipped($shipmentData);
}
}
拾取
模型如下所示:
class Picking extends \Eloquent {
public function order() {
return $this->belongsTo('Order');
}
public function shipped($shipmentData) {
$this->carrier = $shipmentData['Carrier'];
$this->service = $shipmentData['Service'];
$this->is_shipped = true;
$this->save();
$this->order->pickingShipped();
}
}
$order->expects($this->once())->method('pickingShipped')
class Picking extends \Eloquent {
public function order() {
return $this->belongsTo('Order');
}
public function shipped(Order $order, $shipmentData) {
$this->carrier = $shipmentData['Carrier'];
$this->service = $shipmentData['Service'];
$this->is_shipped = true;
$this->save();
$order->pickingShipped();
}
}
$picking = new Picking;
$picking->shipped($picking->order, $shipmentData);
如您所见,此shipped()
方法保存一些数据,然后在其相关的Order
上调用pickingShipped()
方法
现在,我正在尝试为shipped()
方法编写一个测试,但我不确定是否有合适的方法来实现这一点。我读过关于嘲弄的书,但我不知道这是否是一种需要嘲弄的情况。我想到了一些可能的解决办法,但我不确定其中是否有正确的
1)重新排列代码,以便控制器调用pickingShipped()
方法,允许将其从shipped()
方法中删除,从而简化测试。
例如,shipped()
方法的最后一行将被删除,控制器代码将更改为:
$picking = new Picking;
$picking->shipped($shipmentData);
$picking->order->pickingShipped();
2)在测试中,在order
上使用模拟方法,以便测试可以简单地确认调用了pickingShipped()
方法。
与所解释的内容大致相同的东西。这意味着测试可以做如下操作:
class Picking extends \Eloquent {
public function order() {
return $this->belongsTo('Order');
}
public function shipped($shipmentData) {
$this->carrier = $shipmentData['Carrier'];
$this->service = $shipmentData['Service'];
$this->is_shipped = true;
$this->save();
$this->order->pickingShipped();
}
}
$order->expects($this->once())->method('pickingShipped')
class Picking extends \Eloquent {
public function order() {
return $this->belongsTo('Order');
}
public function shipped(Order $order, $shipmentData) {
$this->carrier = $shipmentData['Carrier'];
$this->service = $shipmentData['Service'];
$this->is_shipped = true;
$this->save();
$order->pickingShipped();
}
}
$picking = new Picking;
$picking->shipped($picking->order, $shipmentData);
但是,我认为这意味着我还需要注入订单依赖关系,而不是依赖shipped()
方法中的order
关系,如下所示:
class Picking extends \Eloquent {
public function order() {
return $this->belongsTo('Order');
}
public function shipped($shipmentData) {
$this->carrier = $shipmentData['Carrier'];
$this->service = $shipmentData['Service'];
$this->is_shipped = true;
$this->save();
$this->order->pickingShipped();
}
}
$order->expects($this->once())->method('pickingShipped')
class Picking extends \Eloquent {
public function order() {
return $this->belongsTo('Order');
}
public function shipped(Order $order, $shipmentData) {
$this->carrier = $shipmentData['Carrier'];
$this->service = $shipmentData['Service'];
$this->is_shipped = true;
$this->save();
$order->pickingShipped();
}
}
$picking = new Picking;
$picking->shipped($picking->order, $shipmentData);
然后控制器中的代码必须如下所示:
class Picking extends \Eloquent {
public function order() {
return $this->belongsTo('Order');
}
public function shipped($shipmentData) {
$this->carrier = $shipmentData['Carrier'];
$this->service = $shipmentData['Service'];
$this->is_shipped = true;
$this->save();
$this->order->pickingShipped();
}
}
$order->expects($this->once())->method('pickingShipped')
class Picking extends \Eloquent {
public function order() {
return $this->belongsTo('Order');
}
public function shipped(Order $order, $shipmentData) {
$this->carrier = $shipmentData['Carrier'];
$this->service = $shipmentData['Service'];
$this->is_shipped = true;
$this->save();
$order->pickingShipped();
}
}
$picking = new Picking;
$picking->shipped($picking->order, $shipmentData);
这感觉有点奇怪,但我真的不确定什么是对的
我的问题是,编写和测试此代码的正确方法是什么?测试
shipped()
方法可以很容易地在自身上设置适当的数据,但是最后调用pickingShipped()
会怎么样?这似乎使测试更加复杂。那么代码应该重新排列吗?如果是,怎么做?或者,这是我在第二个选项中概述的模拟的常见用例吗?如果是这样的话,正如我所展示的那样注入依赖项是否正确?我不是PHP开发人员,因此这可能归结为语言特性是一个拦截器
我认为依赖项注入方法更好,因为它调用依赖项,并允许您稍后分离持久性和行为。例如,Picking
或Picker
可能是一个更好的行为名称,而PickingRecord
可能适合数据
在任何情况下,如果您可以在PHP中设置默认参数,那么我喜欢您使用的最后一种方法(注入),您现在可以简化为
public function shipped($shipmentData, Order $order = $this->order) {
$this->carrier = $shipmentData['Carrier'];
$this->service = $shipmentData['Service'];
$this->is_shipped = true;
$this->save();
$order->pickingShipped();
}
然后,这将允许您忽略生产代码中的order
依赖项,并在测试中注入一个double或其他类型的对象作为order
,并简单地断言该方法是在order
对象上调用的。集成测试应该继续监控接口是否仍然在一起,即使您在单元测试中注入了双倍
这就是我在Ruby中尝试的方式 我想出了一个我觉得不错的解决方案。我现在看到这一点似乎很明显。我所做的就是设置
$picking->order
属性来返回测试的模拟订单
$order = Mockery::mock(Order::class);
$picking = new Picking;
$picking->order = $order;
$order->shouldReceive('pickingShipped')
->with($picking)
->once();
$picking->shipped($shipmentData);
现在,当shipped()
方法调用$this->order
时,它将获得我定义的模拟$order
对象,并且测试工作正常
这似乎是正确的解决方案。感谢您的反馈!这种方法的唯一问题是,我们允许将
$order
传递到方法中,而实际上不允许这样做。该方法的要点是,它必须对拣选订单调用pickingShipped
,而不仅仅是任何订单。这意味着我们只是出于测试目的添加此参数。仅仅为了测试目的而更改方法签名感觉很奇怪。现在,该方法建议它应该用于实际不应该使用的行为。关于默认参数,是的,PHP允许您传入它们,但它们不能有像$this->order
这样的动态值。在这种情况下,我会将默认值设置为null
,然后在方法的开头说if(is_null($order))$order=$this->order代码>,这将完成同样的事情,但正如我上面所说的,这种依赖注入方法对我来说仍然不合适。