skip to Main Content

For a Laravel Test of my Product API I am trying to check if the right number of prices are listed for all products. The API returns Json and the prices are items in the products.*.prices arrays.

I am new to Laravel Test.

How do I loop through all the products and check their prices?

$response = $this->getJson('/api/product/');

$json_array = $response->json();
$test_passed = true;

for ($i = 0; $i < count($json_array); $i++) {
  //for simplification I check here if all the products have 2 prices
  $test_passed = $response->assertJsonCount(2, "product.$i.prices");
}

//check if all the test passed
$this->assertTrue($passed_test);

This works but it runs out of memory really fast. What am I doing wrong?

UPDATE:

I found in the Laravel API that AssertableJson has and ->each() method. I was trying to play around with this to make something like

$response->assertJson(fn (AssertableJson $json) =>
  $json->each(fn (AssertableJson $json) =>
    //2 will be dynamic in the final implementation.
    $json->has('prices', 2) 
    )
  );

This doesn’t work, Apperently… I can’t figure out how this ->each() works and there are no examples online to be found. All I can find are the Laravel docs, yet they give no example with nested arrarys.

2

Answers


  1. Since you should be wiping your database clean for every test to start with a clean slate, I think the steps to take are

    1. Create an arbitrary amount of Product models with an arbitrary amount of Price models associated with them
    2. Call the api endpoint
    3. Assert there’s only as many Product models as we specified in step 1.
    4. 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.

    Login or Signup to reply.
  2. Based on your JSON that you posted here: https://jsonblob.com/1087309750307405824
    some minor change can fix your problem

    $response = $this->getJson('/api/product/');
    $test_passed = true;
    
    for ($i = 0; $i < count($response->json("products")); $i++) {
      //for simplification I check here if all the products have 2 prices
      $test_passed = $response->assertJsonCount(2, "products.$i.prices");
    }
    
    //check if all the test passed
    $this->assertTrue($passed_test);
    

    If your code runs out of memory, it could be that you get all products in your database and product JSON size is too large or could be memory limitation in your system.

    For checking php memory usage, you can use memory_get_usage function in your "for loop"

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search