I’m currently working on a Laravel project and I’m facing an issue with setting up a factory for a pivot table. Specifically, I’m trying to create a factory for a table that serves as a pivot between Order
and Product
models. The table is named order_product
and includes fields such as order_id
, product_id
, quantity
, and price
.
The test looks like this:
public function testCalculateOrderTotal(): void
{
$address = Address::factory()->create();
$client = Client::factory()->create(['address_id' => $address->id]);
$order = Order::factory()->create(['client_id' => $client->id]);
$product1 = Product::factory()->create();
$product2 = Product::factory()->create();
$orderProductData1 = [
'order_id' => $order->id,
'product_id' => $product1->id,
'quantity' => 2,
'price' => 100.00
];
$orderProductData2 = [
'order_id' => $order->id,
'product_id' => $product2->id,
'quantity' => 1,
'price' => 150.00
];
$orderProduct1 = OrderProduct::factory()->create($orderProductData1);
$orderProduct2 = OrderProduct::factory()->create($orderProductData2);
$this->orderProductRepository
->shouldReceive('findByOrder')
->with($order->id)
->once()
->andReturn(new Collection([$orderProduct1, $orderProduct2]));
$expectedTotal = ($orderProductData1['quantity'] * $orderProductData1['price']) +
($orderProductData2['quantity'] * $orderProductData2['price']);
$total = $this->orderProductService->calculateOrderTotal($order->id);
$this->assertIsFloat($total);
$this->assertEquals($expectedTotal, $total);
}
and this is the service method which I’m trying to test
public function calculateOrderTotal($order_id): float
{
try {
$orderProducts = $this->orderProductRepository->findByOrder($order_id);
return $orderProducts->sum(fn($item) => $item->quantity * $item->price);
} catch (Throwable $th) {
throw $th;
}
}
and when I run all the tests (./vendor/bin/sail exec -T laravel.test ./vendor/bin/phpunit --coverage-html=coverage/
) it keeps returning:
FAILED TestsUnitServicesOrderProductServiceTest > calculate order total Error
Call to a member function __call() on null
at tests/Unit/Services/OrderProductServiceTest.php:56
➜ 56▕ $orderProduct1 = OrderProduct::factory()->create($orderProductData1);
57▕ $orderProduct2 = OrderProduct::factory()->create($orderProductData2);
58▕
1 tests/Unit/Services/OrderProductServiceTest.php:56
My OrderProductFactory
looks like this:
public function definition(): array
{
return [
'order_id' => Order::factory(),
'product_id' => Product::factory(),
'quantity' => $this->faker->numberBetween(1, 10),
'price' => $this->faker->randomFloat(2, 5, 100),
];
}
I’ve tried to update my OrderProductFactory but that didn’t work.
Any help or guidance would be greatly appreciated!
Thanks in advance!
8
This does not look hard. But before helping you resolve this problem, I got a few questions of my own:
-
How do you instantiate that
$orderProductService
variable? via the service container or just new up the object? -
How do you instantiate that
$orderProductRepository
inside theOrderProductService
class? Is that a dependency like this?
class OrderProductService
{
protected $orderProductRepository;
public function __construct(OrderProductRepository $orderProductRepository)
{
$this->orderProductRepository = $orderProductRepository;
}
}
- I see you are mocking the
OrderProductRepository
class. Reading [the docs][1], it has to be mocked this way:
$this->mock(OrderProductRepository::class, function ($mock) use ($order) {
$mock->shouldReceive('findByOrder')
->with($order->id)
->once()
->andReturn(new Collection([$orderProduct1, $orderProduct2]));
});
Still, I find mocking in this context unnecessary. That findByOrder
method is simple and should return the correct results anyway. You can just remove this line.
Let us know, so we can help you resolve.