dynait's avatar

Test Dependencies

Hello guys,

I am working on Laravel 5.7 and i'd like to ask you a question regarding the PHPUnit testing.

I have a test class, let's say ProductControllerTest, with two methods testProductSoftDelete() and testProductPermanentlyDelete(). I want to use the annotation @depends in the testProductPermanentlyDelete() in order to soft-delete first a product and then get the product id and proceed to permanently deletion test. The problem here is that the DatabaseTransaction trait runs the transactions in every test (method) execution. I need to start the transaction before all the tests of my ProductControllerTest class and then rollback the transaction at the end of all tests. Do you have any ideas? From what i have searched from the web nothing worked properly.

0 likes
7 replies
bencarter78@hotmail.com's avatar

Hi, is there any reason why you are using this approach and not writing 2 independent tests?

dynait's avatar

The tests are independent, but there is a business logic behind that needs first to soft-delete a product and the proceed with the permanently deletion action.

public function testProductSoftDelete()
    {
        $response = $this->actingAsSuperAdmin()->delete('/pages/admin/management/products/soft-delete/xxxxx-sxxx-x-xxxxxxx-xxxx');

       $response ->assertStatus(200);
       $product = $response->json('data');

        return $product ['id'];
    }


    /**
     * @depends testProductSoftDelete
     */
    public function testProductPermanentlyDelete($product_id)
    {
        $response = $this->actingAsSuperAdmin()->delete('/pages/admin/management/products/permanently-delete/'.$product_id);

        $response
            ->assertStatus(200)
            ->assertSeeText('"Product deleted successfully');
    }
dynait's avatar

The second test always fail because of the business logic which checks first if the product is soft-deleted in order to deleted forever. Does make sense to use depends annotations or it is better to create one test with 2 requests ?

dynait's avatar

I was wondering if this approach make sense:


        namespace Tests\App\Controllers\Product;

        use Tests\DatabaseTestCase;
        use Tests\TestRequestsTrait;

        /**
         * @group Coverage
         * @group App.Controllers
         * @group App.Controllers.Product
         *
         * Class ProductControllerTest
         *
         * @package Tests\App\Controllers\Product
         */

        class ProductControllerTest extends DatabaseTestCase
        {
            public function testSoftDelete()
            {
                $response = $this->doProductSoftDelete('9171448');
                $response
                    ->assertStatus(200)
                    ->assertSeeText('Product sof-deleted successfully');
            }


            public function testUnlink()
            {
                $this->doProductSoftDelete('9171448');
                $response = $this->actingAsSuperAdmin()->delete('/pages/admin/management/product/unlink/9171448');

                $response
                    ->assertStatus(200)
                    ->assertSeeText('Product unlinked successfully');
            }
        }


        namespace Tests;

        trait TestRequestsTrait
        {
            /**
             * Returns the response
             *
             * @param $product_id
             * @return \Illuminate\Foundation\Testing\TestResponse
             */
            protected function doProductSoftDelete($product_id)
            {
                $response = $this->actingAsSuperAdmin()->delete('/pages/admin/management/product/soft-delete/'.$product_id);
                return $response;
            }

        }

        namespace Tests;

        use Illuminate\Foundation\Testing\DatabaseTransactions;

        abstract class DatabaseTestCase extends TestCase
        {

            use CreatesApplication;
            use DatabaseTransactions;
            use TestRequestsTrait;
        }




bencarter78@hotmail.com's avatar

Well I would probably approach it more like this.

First, use a Factory to create the product, and by adding

// ProductFactory.php
$factory->define(Product::class, function (Faker $faker) {
    return [
        'name' => $faker->word,
    ];
});

$factory->state(Product::class, 'archived', function () {
    return [
        'deleted_at' => Carbon::now()
    ];
});

And then in my test something like

class ProductControllerTest extends TestCase
{
    use RefreshDatabase;

    /** @test */
    public function it_archives_a_product()
    {
        $product = factory()->create(Product::class);

        $response = $this->actingAsSuperAdmin()->post('/pages/admin/management/products/' . $product->id . '/archive');
        
        $response->assertStatus(200);
        $response->assertSeeText('Product sof-deleted successfully');
        $this->assertTrue($product->fresh()->trashed());
    }

    /** @test */
    public function it_deletes_a_product()
    {
        $product = factory()->states('archived')->create(Product::class);

        $response = $this->actingAsSuperAdmin()->delete('/pages/admin/management/products/' . $product->id);
        
        $response->assertStatus(200);
        $response->assertSeeText('Product deleted successfully');
        $this->assertCount(0, Product::count());
    }
}
dynait's avatar

Nice idea. I made a similar implementation by using a trait in my tests as the main factory that returns each time the relavant models for the specific tests.

But, the reason that i can't use the depends annotation in my tests it's an annoying issue.

Thank you any way.

Please or to participate in this conversation.