Since you should be wiping your database clean for every test to start with a clean slate, I think the steps to take are
- Create an arbitrary amount of
Product
models with an arbitrary amount of Price
models associated with them
- Call the api endpoint
- Assert there's only as many
Product
models as we specified in step 1.
- Assert each
Product
has as many Price
models associated with them as we specified in step 1.
public function test_products_api_returns_correct_amount_of_products()
{
// ARRANGE
Product::factory()->count(15)->create()
// ACT
$response = $this->getJson('/api/product/');
// ASSERT
$response->assertJsonCount(15, 'products');
}
public function test_products_api_returns_correct_amount_of_prices_for_each_product()
{
// ARRANGE
$counts = [1, 3, 5, 7, 10];
foreach ($counts as $count) {
Product::factory()
->has(Price::factory()->count($count)) // or ->hasPrices($count)
->create();
}
// ACT
$response = $this->getJson('/api/product/');
// ASSERT
foreach ($counts as $index => $count) {
$response->assertJsonCount($count, "products.$index.prices");
}
}
Here notice I'm using the key
value of the array in the foreach. This is not possible with the AssertableJson
syntax because the Closure does not accept a second parameter (unlike the Collection
's each
method where I could use the key if I wanted to with $collection->each(function ($item, $key) { ... });
.
The AssertableJson API is a bit limited so you cannot really use it for this test, unless you make a single type of product.
public function test_products_api_returns_correct_amount_of_prices_for_each_product()
{
// ARRANGE
Product::factory()
->count(15)
->has(Price::factory()->count(5))
->create();
}
// ACT
$response = $this->getJson('/api/product/');
// ASSERT
$response->assertJson(fn (AssertableJson $json) =>
$json->has('products', 15, fn (AssertableJson $product) =>
$product->count('prices', 5)->etc()
)
);
// or
$response->assertJson(fn (AssertableJson $json) =>
$json->has('products', 15, fn (AssertableJson $product) =>
$product->has('prices', 5, fn (AssertableJson $price) =>
// assertions about the products.*.prices.* array.
)->etc()
)
);
}
Another test you can create and that is sort of important for json apis is a test against the returned json structure. In your case it would look like this
public function test_products_api_returns_the_correct_structure()
{
// ARRANGE
... make all the product, prices, options, etc
// ACT
$response = $this->getJson('/api/product/');
// ASSERT
$response->assertJsonStructure([
'products' => [
'*' => [
'product_id',
'name',
'description',
'included',
'is_active',
'prices' => [
'*' => [
'price_id',
'weight',
'height',
'length',
'depth',
'pieces',
'color',
'price',
'start_time',
'end_time',
'sales' => [
'*' => [
'sale_id',
'sale_price',
'sale_start_time',
'sale_end_time',
],
],
],
],
'photos' => [
'*' => [
'photo_id',
'alt',
'path',
],
],
'properties' => [
'*' => [
'property_id',
'name',
'description',
'quantitative',
'position',
'is_active',
'properties_properties' => [
'*' => [
'properties_property_id',
'name',
'icon',
'path',
'attributes' => [
'*' => [
'product_id',
'properties_property_id',
'position',
'highlight',
],
],
],
],
],
],
'options' => [
'*' => [
'option_id',
'name',
'place_holder',
'position',
'is_active',
'options_options' => [
'*' => [
'options_option_id',
'name',
'position',
'price',
'is_active',
],
],
],
],
],
],
]);
}
I tested this last one against the json you posted (jsonblob.com/1087309750307405824) and it passes.