使用 findOrFail 时得到 "Trying to get property 'subtotal' of non-object"
Getting "Trying to get property 'subtotal' of non-object" when using findOrFail
我在服务中得到了以下代码:
public function add(array $data) : Order
{
// retrieve item data
$item = MenuItem::findOrFail($data[OrderItem::ORDER_ITEM_ITEM_ID]);
$quantity = $data[OrderItem::ORDER_ITEM_QUANTITY];
$order_no = $data[Order::ORDER_NO] ?? null;
$session_id = $data[Order::ORDER_SESSION_ID];
$order = $order_no ? Order::findOrFail($order_no) : $this->createOrder([Order::ORDER_SESSION_ID => $session_id]);
$order_item = $this->order_item->createOrderItem($order->order_no, $item, $quantity);
// update order total
$order->subtotal += $order_item->subtotal;
$order->total += $order_item->subtotal;
$order->update();
return $order;
}
当 运行 我得到的测试:
Getting "Trying to get property 'subtotal' of non-object"
检索到的对象不为空,否则检索失败。但是小计和总计都不属于该对象。如果我打印对象,我会得到除这两个之外的其他字段。它们都在我的 $fillable
数组中,我在迁移中定义了一个 ->default(0);
。
怎么回事?
编辑
PHP单位输出如下:
PHPUnit 8.5.0 by Sebastian Bergmann and contributors.
array:5 [
"message" => "Trying to get property 'subtotal' of non-object"
"exception" => "ErrorException"
"file" => "/Users/bigweld/Sites/restaurantbe/app/Services/OrderService.php"
"line" => 110
"trace" => array:33 [
0 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/app/Services/OrderService.php"
"line" => 110
"function" => "handleError"
"class" => "Illuminate\Foundation\Bootstrap\HandleExceptions"
"type" => "->"
]
1 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/app/Http/Controllers/API/OrderController.php"
"line" => 57
"function" => "add"
"class" => "App\Services\OrderService"
"type" => "->"
]
2 => array:3 [
"function" => "add"
"class" => "App\Http\Controllers\API\OrderController"
"type" => "->"
]
3 => array:3 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Routing/Controller.php"
"line" => 54
"function" => "call_user_func_array"
]
4 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php"
"line" => 45
"function" => "callAction"
"class" => "Illuminate\Routing\Controller"
"type" => "->"
]
5 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Routing/Route.php"
"line" => 219
"function" => "dispatch"
"class" => "Illuminate\Routing\ControllerDispatcher"
"type" => "->"
]
6 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Routing/Route.php"
"line" => 176
"function" => "runController"
"class" => "Illuminate\Routing\Route"
"type" => "->"
]
7 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Routing/Router.php"
"line" => 681
"function" => "run"
"class" => "Illuminate\Routing\Route"
"type" => "->"
]
8 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
"line" => 130
"function" => "Illuminate\Routing\{closure}"
"class" => "Illuminate\Routing\Router"
"type" => "->"
]
9 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
"line" => 105
"function" => "Illuminate\Pipeline\{closure}"
"class" => "Illuminate\Pipeline\Pipeline"
"type" => "->"
]
10 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Routing/Router.php"
"line" => 683
"function" => "then"
"class" => "Illuminate\Pipeline\Pipeline"
"type" => "->"
]
11 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Routing/Router.php"
"line" => 658
"function" => "runRouteWithinStack"
"class" => "Illuminate\Routing\Router"
"type" => "->"
]
12 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Routing/Router.php"
"line" => 624
"function" => "runRoute"
"class" => "Illuminate\Routing\Router"
"type" => "->"
]
13 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Routing/Router.php"
"line" => 613
"function" => "dispatchToRoute"
"class" => "Illuminate\Routing\Router"
"type" => "->"
]
14 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php"
"line" => 177
"function" => "dispatch"
"class" => "Illuminate\Routing\Router"
"type" => "->"
]
15 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
"line" => 130
"function" => "Illuminate\Foundation\Http\{closure}"
"class" => "Illuminate\Foundation\Http\Kernel"
"type" => "->"
]
16 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
"line" => 105
"function" => "Illuminate\Pipeline\{closure}"
"class" => "Illuminate\Pipeline\Pipeline"
"type" => "->"
]
17 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php"
"line" => 152
"function" => "then"
"class" => "Illuminate\Pipeline\Pipeline"
"type" => "->"
]
18 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php"
"line" => 117
"function" => "sendRequestThroughRouter"
"class" => "Illuminate\Foundation\Http\Kernel"
"type" => "->"
]
19 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php"
"line" => 434
"function" => "handle"
"class" => "Illuminate\Foundation\Http\Kernel"
"type" => "->"
]
20 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php"
"line" => 406
"function" => "call"
"class" => "Illuminate\Foundation\Testing\TestCase"
"type" => "->"
]
21 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/tests/Feature/OrdersTest.php"
"line" => 428
"function" => "json"
"class" => "Illuminate\Foundation\Testing\TestCase"
"type" => "->"
]
22 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/src/Framework/TestCase.php"
"line" => 1408
"function" => "when_adding_the_same_item_twice_to_an_empty_order_then_only_one_order_item_is_created_but_its_quantity_increases"
"class" => "Tests\Feature\OrdersTest"
"type" => "->"
]
23 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/src/Framework/TestCase.php"
"line" => 1028
"function" => "runTest"
"class" => "PHPUnit\Framework\TestCase"
"type" => "->"
]
24 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/src/Framework/TestResult.php"
"line" => 691
"function" => "runBare"
"class" => "PHPUnit\Framework\TestCase"
"type" => "->"
]
25 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/src/Framework/TestCase.php"
"line" => 756
"function" => "run"
"class" => "PHPUnit\Framework\TestResult"
"type" => "->"
]
26 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/src/Framework/TestSuite.php"
"line" => 597
"function" => "run"
"class" => "PHPUnit\Framework\TestCase"
"type" => "->"
]
27 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/src/Framework/TestSuite.php"
"line" => 597
"function" => "run"
"class" => "PHPUnit\Framework\TestSuite"
"type" => "->"
]
28 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/src/Framework/TestSuite.php"
"line" => 597
"function" => "run"
"class" => "PHPUnit\Framework\TestSuite"
"type" => "->"
]
29 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/src/TextUI/TestRunner.php"
"line" => 621
"function" => "run"
"class" => "PHPUnit\Framework\TestSuite"
"type" => "->"
]
30 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/src/TextUI/Command.php"
"line" => 200
"function" => "doRun"
"class" => "PHPUnit\TextUI\TestRunner"
"type" => "->"
]
31 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/src/TextUI/Command.php"
"line" => 159
"function" => "run"
"class" => "PHPUnit\TextUI\Command"
"type" => "->"
]
32 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/phpunit"
"line" => 61
"function" => "main"
"class" => "PHPUnit\TextUI\Command"
"type" => "::"
]
]
]
根据我们在评论中的对话,您的函数中存在的问题之一,并因此导致您的测试出现问题,是无法保证每次结果的依赖性。也就是说,您在 add
函数中对其他服务的调用无法保证 提供特定输出。
这样想你的测试。
- 我从零件商品和零小计的订单开始
- 我想在该订单中添加一个项目
- 我想以一件商品和 10 美元小计的订单结束
在此测试中,您不关心如何创建订单项,您只关心它是创建的,与一个特定的值,以便在将其添加到您的订单时,最终结果就是您所期望的。
在这种情况下,您希望 $this->order_item
成为一个模拟对象,然后您可以将对 createOrderItem
的调用模拟为 return 一个已知的订单对象。向您展示一个完整的解决方案有点困难,因为它可能涉及一些架构更改,但您的测试将类似于:
public function it_adds_an_item_to_an_order()
{
$orderItem = Mock(OrderItem::class); // Use whatever mocking lib is available
$orderitem->shouldReceive('createOrderItem').andReturn(new OrderItem([$subtotal=>10.00]));
$orderService = new OrderService($orderItem);
$order = $orderService->add($data);
$this->assertNotNull($order);
$this->assertEquals(10.00, $order->subtotal);
}
希望这对您有所帮助。
我在服务中得到了以下代码:
public function add(array $data) : Order
{
// retrieve item data
$item = MenuItem::findOrFail($data[OrderItem::ORDER_ITEM_ITEM_ID]);
$quantity = $data[OrderItem::ORDER_ITEM_QUANTITY];
$order_no = $data[Order::ORDER_NO] ?? null;
$session_id = $data[Order::ORDER_SESSION_ID];
$order = $order_no ? Order::findOrFail($order_no) : $this->createOrder([Order::ORDER_SESSION_ID => $session_id]);
$order_item = $this->order_item->createOrderItem($order->order_no, $item, $quantity);
// update order total
$order->subtotal += $order_item->subtotal;
$order->total += $order_item->subtotal;
$order->update();
return $order;
}
当 运行 我得到的测试:
Getting "Trying to get property 'subtotal' of non-object"
检索到的对象不为空,否则检索失败。但是小计和总计都不属于该对象。如果我打印对象,我会得到除这两个之外的其他字段。它们都在我的 $fillable
数组中,我在迁移中定义了一个 ->default(0);
。
怎么回事?
编辑
PHP单位输出如下:
PHPUnit 8.5.0 by Sebastian Bergmann and contributors.
array:5 [
"message" => "Trying to get property 'subtotal' of non-object"
"exception" => "ErrorException"
"file" => "/Users/bigweld/Sites/restaurantbe/app/Services/OrderService.php"
"line" => 110
"trace" => array:33 [
0 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/app/Services/OrderService.php"
"line" => 110
"function" => "handleError"
"class" => "Illuminate\Foundation\Bootstrap\HandleExceptions"
"type" => "->"
]
1 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/app/Http/Controllers/API/OrderController.php"
"line" => 57
"function" => "add"
"class" => "App\Services\OrderService"
"type" => "->"
]
2 => array:3 [
"function" => "add"
"class" => "App\Http\Controllers\API\OrderController"
"type" => "->"
]
3 => array:3 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Routing/Controller.php"
"line" => 54
"function" => "call_user_func_array"
]
4 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php"
"line" => 45
"function" => "callAction"
"class" => "Illuminate\Routing\Controller"
"type" => "->"
]
5 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Routing/Route.php"
"line" => 219
"function" => "dispatch"
"class" => "Illuminate\Routing\ControllerDispatcher"
"type" => "->"
]
6 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Routing/Route.php"
"line" => 176
"function" => "runController"
"class" => "Illuminate\Routing\Route"
"type" => "->"
]
7 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Routing/Router.php"
"line" => 681
"function" => "run"
"class" => "Illuminate\Routing\Route"
"type" => "->"
]
8 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
"line" => 130
"function" => "Illuminate\Routing\{closure}"
"class" => "Illuminate\Routing\Router"
"type" => "->"
]
9 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
"line" => 105
"function" => "Illuminate\Pipeline\{closure}"
"class" => "Illuminate\Pipeline\Pipeline"
"type" => "->"
]
10 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Routing/Router.php"
"line" => 683
"function" => "then"
"class" => "Illuminate\Pipeline\Pipeline"
"type" => "->"
]
11 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Routing/Router.php"
"line" => 658
"function" => "runRouteWithinStack"
"class" => "Illuminate\Routing\Router"
"type" => "->"
]
12 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Routing/Router.php"
"line" => 624
"function" => "runRoute"
"class" => "Illuminate\Routing\Router"
"type" => "->"
]
13 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Routing/Router.php"
"line" => 613
"function" => "dispatchToRoute"
"class" => "Illuminate\Routing\Router"
"type" => "->"
]
14 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php"
"line" => 177
"function" => "dispatch"
"class" => "Illuminate\Routing\Router"
"type" => "->"
]
15 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
"line" => 130
"function" => "Illuminate\Foundation\Http\{closure}"
"class" => "Illuminate\Foundation\Http\Kernel"
"type" => "->"
]
16 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
"line" => 105
"function" => "Illuminate\Pipeline\{closure}"
"class" => "Illuminate\Pipeline\Pipeline"
"type" => "->"
]
17 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php"
"line" => 152
"function" => "then"
"class" => "Illuminate\Pipeline\Pipeline"
"type" => "->"
]
18 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php"
"line" => 117
"function" => "sendRequestThroughRouter"
"class" => "Illuminate\Foundation\Http\Kernel"
"type" => "->"
]
19 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php"
"line" => 434
"function" => "handle"
"class" => "Illuminate\Foundation\Http\Kernel"
"type" => "->"
]
20 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php"
"line" => 406
"function" => "call"
"class" => "Illuminate\Foundation\Testing\TestCase"
"type" => "->"
]
21 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/tests/Feature/OrdersTest.php"
"line" => 428
"function" => "json"
"class" => "Illuminate\Foundation\Testing\TestCase"
"type" => "->"
]
22 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/src/Framework/TestCase.php"
"line" => 1408
"function" => "when_adding_the_same_item_twice_to_an_empty_order_then_only_one_order_item_is_created_but_its_quantity_increases"
"class" => "Tests\Feature\OrdersTest"
"type" => "->"
]
23 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/src/Framework/TestCase.php"
"line" => 1028
"function" => "runTest"
"class" => "PHPUnit\Framework\TestCase"
"type" => "->"
]
24 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/src/Framework/TestResult.php"
"line" => 691
"function" => "runBare"
"class" => "PHPUnit\Framework\TestCase"
"type" => "->"
]
25 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/src/Framework/TestCase.php"
"line" => 756
"function" => "run"
"class" => "PHPUnit\Framework\TestResult"
"type" => "->"
]
26 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/src/Framework/TestSuite.php"
"line" => 597
"function" => "run"
"class" => "PHPUnit\Framework\TestCase"
"type" => "->"
]
27 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/src/Framework/TestSuite.php"
"line" => 597
"function" => "run"
"class" => "PHPUnit\Framework\TestSuite"
"type" => "->"
]
28 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/src/Framework/TestSuite.php"
"line" => 597
"function" => "run"
"class" => "PHPUnit\Framework\TestSuite"
"type" => "->"
]
29 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/src/TextUI/TestRunner.php"
"line" => 621
"function" => "run"
"class" => "PHPUnit\Framework\TestSuite"
"type" => "->"
]
30 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/src/TextUI/Command.php"
"line" => 200
"function" => "doRun"
"class" => "PHPUnit\TextUI\TestRunner"
"type" => "->"
]
31 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/src/TextUI/Command.php"
"line" => 159
"function" => "run"
"class" => "PHPUnit\TextUI\Command"
"type" => "->"
]
32 => array:5 [
"file" => "/Users/bigweld/Sites/restaurantbe/vendor/phpunit/phpunit/phpunit"
"line" => 61
"function" => "main"
"class" => "PHPUnit\TextUI\Command"
"type" => "::"
]
]
]
根据我们在评论中的对话,您的函数中存在的问题之一,并因此导致您的测试出现问题,是无法保证每次结果的依赖性。也就是说,您在 add
函数中对其他服务的调用无法保证 提供特定输出。
这样想你的测试。
- 我从零件商品和零小计的订单开始
- 我想在该订单中添加一个项目
- 我想以一件商品和 10 美元小计的订单结束
在此测试中,您不关心如何创建订单项,您只关心它是创建的,与一个特定的值,以便在将其添加到您的订单时,最终结果就是您所期望的。
在这种情况下,您希望 $this->order_item
成为一个模拟对象,然后您可以将对 createOrderItem
的调用模拟为 return 一个已知的订单对象。向您展示一个完整的解决方案有点困难,因为它可能涉及一些架构更改,但您的测试将类似于:
public function it_adds_an_item_to_an_order()
{
$orderItem = Mock(OrderItem::class); // Use whatever mocking lib is available
$orderitem->shouldReceive('createOrderItem').andReturn(new OrderItem([$subtotal=>10.00]));
$orderService = new OrderService($orderItem);
$order = $orderService->add($data);
$this->assertNotNull($order);
$this->assertEquals(10.00, $order->subtotal);
}
希望这对您有所帮助。