Codeception / module-laravel

Modern Laravel module for Codeception
MIT License
5 stars 13 forks source link

Laravel module + Doctrine ORM cleanup not working #17

Open 99hops opened 4 years ago

99hops commented 4 years ago

What are you trying to achieve?

Rollback transactions between each test

What do you get instead?

Database changes not reverted

Provide console output if related. Use -vvv mode for more details.

vendor/bin/codecept -vvv run api
Codeception PHP Testing Framework v4.1.4
Powered by PHPUnit 8.5.4 by Sebastian Bergmann and contributors.
Running with seed: 

Api Tests (2) --------------------------------------------------------------------------------------------------------
Modules: \Helper\Api, Cli, Laravel5, REST
----------------------------------------------------------------------------------------------------------------------
Recreating database...
Dropped all tables successfully.                                                                       
Database seeding completed successfully.                                                                            
done!
UserRegistrationCest: Registration success test
Signature: UserRegistrationCest:registrationSuccessTest
Test: tests/api/UserRegistrationCest.php:registrationSuccessTest
Scenario --
**[Database] Transaction started**
I send post "http://api.project.local/v1/auth/register",{"email":"davert@codeception.com","phone":"+380670000222","pa...}
[Request] POST http://api.project.local/v1/auth/register {"email":"davert@codeception.com","phone":"+380670000222","password":"password","firstName":"firstName","lastName":"firstName","username":"username","inviteHash":"invite","nationality":"nationality","country":"USA","city":"NY","street":"Park Avenue","homeNumber":"256","zip":"23699","birthDate":"1988-01-01"}                                                                                                             
[Request Headers] []
[Page] http://api.project.local/v1/auth/register
[Response] 201
[Request Cookies] []
[Response Headers] {"cache-control":["no-cache, private"],"date":["Fri, 08 May 2020 13:23:10 GMT"],"content-type":["application/json"]}                                                                                                   
[Response] {"result":{},"version":"v0.0.1","memory-c":40.9822,"memory-p":41.1235}
I see response code is 201
I see response is json 
I can see response contains json {"result":[]}
PASSED 

**[Database] Transaction cancelled; all changes reverted.**

UserRegistrationCest: Registration success2 test
Signature: UserRegistrationCest:registrationSuccess2Test
Test: tests/api/UserRegistrationCest.php:registrationSuccess2Test
Scenario --
**[Database] Transaction started**
I send post "http://api.project.local/v1/auth/register",{"email":"davert@codeception.com","phone":"+380670000222","pa...}
[Request] POST http://api.project.local/v1/auth/register {"email":"davert@codeception.com","phone":"+380670000222","password":"password","firstName":"firstName","lastName":"firstName","username":"username","inviteHash":"invite","nationality":"nationality","country":"USA","city":"NY","street":"Park Avenue","homeNumber":"256","zip":"23699","birthDate":"1988-01-01"}                                                                                                             
[Request Headers] []
[Page] http://api.project.local/v1/auth/register
[Response] 422
[Request Cookies] []
[Response Headers] {"cache-control":["no-cache, private"],"date":["Fri, 08 May 2020 13:23:11 GMT"],"content-type":["application/json"],"access-control-allow-origin":[null],"vary":["Origin"]}                                            
[Response] {"version":"v0.0.1","messages":[{"severity":"error","text":"The given data was invalid.","code":422}],"validation":{"phone":["The phone has already been taken."],"username":["The username has already been taken."],"email":["The email has already been taken."]},"memory-c":43.3535,"memory-p":43.3658}                                          
I see response code is 201
FAIL 

**[Database] Transaction cancelled; all changes reverted.**
----------------------------------------------------------------------------------------------------------------------
Tests ran into 'codeception' environtment

Time: 11.5 seconds, Memory: 44.50 MB

There was 1 failure:

---------
1) UserRegistrationCest: Registration success2 test
Test  tests/api/UserRegistrationCest.php:registrationSuccess2Test
Step  See response code is 201
Fail  Expected HTTP Status Code: 201 (Created). Actual Status Code: 422 (Unprocessable Entity)
Failed asserting that 422 matches expected 201.

Scenario Steps:                                                                                                       

2. $I->seeResponseCodeIs(201) at tests/api/UserRegistrationCest.php:56
1. $I->sendPOST("http://api.project.local/v1/auth/register",{"email":"davert@codeception.com","phone":"+380670000222...})at tests/api/UserRegistrationCest.php:41

/www/refactor/vendor/phpunit/phpunit/src/Framework/Constraint/IsEqual.php:96
/www/refactor/vendor/phpunit/phpunit/src/Framework/Assert.php:2887
/www/refactor/vendor/phpunit/phpunit/src/Framework/Assert.php:602
/www/refactor/vendor/codeception/lib-asserts/src/Codeception/Util/Shared/Asserts.php:41
/www/refactor/vendor/codeception/lib-innerbrowser/src/Codeception/Lib/InnerBrowser.php:1671
/www/refactor/vendor/codeception/module-rest/src/Codeception/Module/REST.php:1269
/www/refactor/vendor/codeception/codeception/src/Codeception/Step.php:268
/www/refactor/vendor/codeception/codeception/src/Codeception/Scenario.php:76
/www/refactor/tests/_support/_generated/ApiTesterActions.php:3351
/www/refactor/tests/api/UserRegistrationCest.php:56
/www/refactor/vendor/codeception/codeception/src/Codeception/Lib/Di.php:127
/www/refactor/vendor/codeception/codeception/src/Codeception/Test/Cest.php:138
/www/refactor/vendor/codeception/codeception/src/Codeception/Test/Cest.php:150
/www/refactor/vendor/codeception/codeception/src/Codeception/Test/Cest.php:82
/www/refactor/vendor/codeception/codeception/src/Codeception/Test/Test.php:88
/www/refactor/vendor/phpunit/phpunit/src/Framework/TestSuite.php:597
/www/refactor/vendor/codeception/phpunit-wrapper/src/Runner.php:117
/www/refactor/vendor/codeception/codeception/src/Codeception/SuiteManager.php:161
/www/refactor/vendor/codeception/codeception/src/Codeception/Codecept.php:196
/www/refactor/vendor/codeception/codeception/src/Codeception/Codecept.php:163
/www/refactor/vendor/codeception/codeception/src/Codeception/Command/Run.php:503
/www/refactor/vendor/codeception/codeception/src/Codeception/Command/Run.php:397
/www/refactor/vendor/symfony/console/Command/Command.php:255
/www/refactor/vendor/symfony/console/Application.php:1001
/www/refactor/vendor/symfony/console/Application.php:271
/www/refactor/vendor/symfony/console/Application.php:147
/www/refactor/vendor/codeception/codeception/src/Codeception/Application.php:117
/www/refactor/vendor/codeception/codeception/app.php:46
/www/refactor/vendor/codeception/codeception/app.php:47
/www/refactor/vendor/codeception/codeception/codecept:7

Artifacts:

Html: /www/refactor/tests/_output/UserRegistrationCest.registrationSuccess2Test.fail.json
Response: /www/refactor/tests/_output/UserRegistrationCest.registrationSuccess2Test.fail.json
Body: {"version":"v0.0.1","messages":[{"severity":"error","text":"The given data was invalid.","code":422}],"validation":{"phone":["The phone has already been taken."],"username":["The username has already been taken."],"email":["The email has already been taken."]},"memory-c":43.3535,"memory-p":43.3658}

FAILURES!
Tests: 2, Assertions: 5, Failures: 1.

Provide test source code if related

<?php

/**
 * Class UserRegistrationCest
 */
class UserRegistrationCest
{

    /**
     * @param ApiTester $I
     */
    public function registrationSuccessTest(ApiTester $I)
    {
        $I->sendPOST(route('api.v1.auth.register'), [
            'email'       => 'davert@codeception.com',
            'phone'       => '+380670000222',
            'password'    => 'password',
            'firstName'   => 'firstName',
            'lastName'    => 'firstName',
            'username'    => 'username',
            'inviteHash'  => 'invite',
            'nationality' => 'nationality',
            'country'     => 'USA',
            'city'        => 'NY',
            'street'      => 'Park Avenue',
            'homeNumber'  => '256',
            'zip'         => '23699',
            'birthDate'   => '1988-01-01',
        ]);
        $I->seeResponseCodeIs(\Codeception\Util\HttpCode::CREATED); // 201
        $I->seeResponseIsJson();
        $I->canSeeResponseContainsJson(['result' => []]);
    }

    /**
     * @param ApiTester $I
     */
    public function registrationSuccess2Test(ApiTester $I)
    {
        $I->sendPOST(route('api.v1.auth.register'), [
            'email'       => 'davert@codeception.com',
            'phone'       => '+380670000222',
            'password'    => 'password',
            'firstName'   => 'firstName',
            'lastName'    => 'firstName',
            'username'    => 'username',
            'inviteHash'  => 'invite',
            'nationality' => 'nationality',
            'country'     => 'USA',
            'city'        => 'NY',
            'street'      => 'Park Avenue',
            'homeNumber'  => '256',
            'zip'         => '23699',
            'birthDate'   => '1988-01-01',
        ]);
        $I->seeResponseCodeIs(\Codeception\Util\HttpCode::CREATED); // 201
        $I->seeResponseIsJson();
        $I->canSeeResponseContainsJson(['result' => []]);
    }
}

Details

actor: ApiTester
modules:
    enabled:
        - \Helper\Api
        - Cli
        #- Db
        #- Doctrine2:
            #connection_callback: ['Doctrine\Common\Persistence\ManagerRegistry', 'getManager']
            #connection_callback: ['Doctrine\ORM\EntityManagerInterface', 'getConnection']
            #cleanup: true
        - Laravel5:
            environment_file: .env.codeception
            cleanup: true #wrap each test in ORM transaction
            #depends: Doctrine2
        - REST:
            url: http://api.project.local/v1/
            depends: Laravel5
            part: Json
extensions:
  enabled:
    - \Extension\Api

At the end the first test passed but the second one which is trying to insert the same db record fails and the previous one is not removed.

What's specific in this project is that is uses Laravel (6.18.13) with Doctrine (1.5 provided by laravel-doctrine/orm). I tried everything possible I could find without success.

What's obvious is [Database] Transaction started / [Database] Transaction cancelled; all changes reverted.

Could it be that the Laravel5 module only supports Eloquent?

Refering here https://codeception.com/for/laravel#functional-tests it is stated: "Codeception will also use Eloquent to cleanup changes to database by wrapping tests into transaction and rolling it back in the end of a test. This makes tests isolated and fast. Laravel5 module allows to access services from DIC container, user authentication methods, fixture generators, check form validations and more."

If so what options do I have to make this work with Doctrine?

Naktibalda commented 3 years ago

Have you got it working?

Dependencies were messed up in that configuration. Doctrine2 module must depend on framework module, but Laravel5 module (and Laravel module now) does not implement DoctrineProvider interface, so you have to implement helper module which implements DoctrineProvider interface and retrieves the instance of Doctrine Entity Manager from Laravel module.

Naktibalda commented 3 years ago

@TavoNiievez Would it be possible to implement DoctrineProvider interface in Laravel module?

Symfony implementation

TavoNiievez commented 3 years ago

Would it be possible to implement the DoctrineProvider interface in the Laravel module?

Absolutely yes.

However, by far, that's not the biggest challenge.

The most popular way to integrate the Doctrine ORM into Laravel is with the lavarel-doctrine project. There are complete but somewhat outdated guides with which you can achieve this in the test project, updating some things like the validation.

To implement this method, we should also check that the integration of the Doctrine and Laravel modules works in their entirety.

I can gladly guide the process if anyone is interested in forking the test project and integrating Doctrine for the two Laravel versions that are currently being tested (6.x LTS and the most recent 8.x main).

From there the implementation of _getEntityManager() can be carried out, and tests could be written to verify the integration, as well as update create the corresponding documentation.

TavoNiievez commented 2 years ago

What's wrong with this issue is that, for some reason, the _before hook of the Doctrine module executes before the one of the Laravel module, while the _after hook of the Doctrine module never executes.

Adding the following makes both the Doctrine integration and the cleanup: true option work:

    public function _getEntityManager()
    {
        if (!$this->client) {
            $this->client = new LaravelConnector($this);
        }
        return $this->app['em'];
    }

and...

    public function _before(TestInterface $test)
    {
        $this->client = new LaravelConnector($this);

        $this->getModule('Doctrine2')->_before($test);
    }

    public function _after(TestInterface $test)
    {
        $this->getModule('Doctrine2')->_after($test);
    }

Of course, this is not the best solution... It is still necessary to check details such as that the memory is cleaned correctly.

But now that there is where to do tests ( https://github.com/TavoNiievez/laravel-module-tests/tree/doctrine ) it should be easier to solve these problems.