Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Yii2 rework, new PR same changes. #4894

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 31, 2018
Merged

Conversation

SamMousa
Copy link
Collaborator

  • No more static variables
  • No more database connection caching (that didn't actually work)
  • Support for multiple database connections / database services with names other than 'db'

This needs more testing; I've tested it in a few of my production apps.

Also somewhere in the docs we should update the "contract" about application state:

  1. When a test starts you get a fresh application.
  2. Just before a request is executed the Request and Response component are reloaded.
  3. Application state is persisted after the request and reused for the next request (as long as it is not in the Request or Response component.
  4. All PDO based connections are supported as long as they are subclasses of yii\db\Connection and fire the AFTER_OPEN event.

Database handling (for transactions)

  1. All caches are cleared at the end of a test.
  2. At the start of a test we register an event handler for AFTER_OPEN.
  3. When the event is fired we generate a cache key based on connection settings.
  4. We store the PDO object in the cache.
  5. We store an entry in the dsn cache, this is used to identify unsupported cases (think connecting to the same database with different credentials, in which case the transaction creates 2 world views which could cause issues).
  6. If we detect a duplicate PDO object we replace the new PDO with the existing one, this causes the new connection to be closed immediately and guarantees us that the new Connection object uses the same PDO object and thus has the same world view.

@DavertMik
Copy link
Member

Also somewhere in the docs we should update the "contract" about application state:

Docblock of a Codeception\Module\Yii2 class is a good place for it.

I want to ask you few things:

  • update documentation for helper
  • list some breaking/minor changes so I could include them into changelog
  • merge with 2.4 to fix travis.yml conflict
  • provide some API for helpers so inside a helper we could access current Yii application (or maybe some other Yii services)

@samdark
Copy link
Member

samdark commented Mar 29, 2018

https://codeception.com/for/yii should be checked i.e. if everything there is still valid with these changes.

@SamMousa
Copy link
Collaborator Author

list some breaking/minor changes so I could include them into changelog

The changes are mainly in the configuration:

  • If you connect to the same DSN with different settings you might receive an error. You must explicitly confirm that you are aware that these connections (even though they connect to the same database) will not share the same world view.
    While this change is a breaking one, I think it won't apply to a lot of users.

provide some API for helpers so inside a helper we could access current Yii application (or maybe some other Yii services)

In Yii2 the application is globally available via Yii::$app. While we could make it available via an alternative route as well this doesn't really offer any advantage. Because we want to support AR Yii::$app must be set anyway.

@samdark samdark added the Yii label Mar 29, 2018
@SamMousa
Copy link
Collaborator Author

https://codeception.com/for/yii should be checked i.e. if everything there is still valid with these changes.

I still have to look into the different parts of a module (like orm, email), not sure how that works yet.

@DavertMik
Copy link
Member

@SamMousa could you put the changes into Changelog.md to make sure no one faces some unpredicted breaks

@SamMousa
Copy link
Collaborator Author

Will do tomorrow.

@SamMousa
Copy link
Collaborator Author

@DavertMik I have added docs!

CHANGELOG-2.4.md Outdated
@@ -15,6 +15,7 @@
* More reliable application state before and during test execution
* Fixtures method is now configurable
* Subset of misconfigurations are now detected and create informative messages
* Application no longer available via the `$module->app`, now you must use `\Yii::$app` everywhere
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Application is no longer available

* More reliable application state before and during test execution
* Fixtures method is now configurable
* Subset of misconfigurations are now detected and create informative messages
* Application is no longer available via the `$module->app`, now you must use `\Yii::$app` everywhere
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a breaking change :(

@DavertMik
Copy link
Member

Ok, got it. Thanks!

@DavertMik DavertMik merged commit 08e487b into Codeception:2.4 Mar 31, 2018
@metalagman
Copy link

@SamMousa please note that your changes break the app lifecycle. Currently I'm bootstraping the following code in my api modules:

Yii::$app->response->on(Response::EVENT_BEFORE_SEND, function ($event) {
            if (Yii::$app->request->url == '/rest' || Yii::$app->request->url == '/rest/') {
                return;
            }

            /** @var Response $response */
            $response = $event->sender;
            $response->format = Response::FORMAT_JSON;
            if ($response->statusCode < 400) {
                $response->data = [
                    'success' => true,
                    'status' => $response->statusCode,
                    'data' => $response->data,
                ];
            } else {
                ArrayHelper::remove($response->data, 'status');
                ArrayHelper::remove($response->data, 'type');
                $response->data = [
                    'success' => false,
                    'status' => $response->statusCode,
                    'data' => $response->data,
                ];
            }
        });

The following tests does not work anymore:

        $I->seeResponseCodeIs(200);
        $I->seeResponseIsJson();
        $I->seeResponseContainsJson(['success' => true]);

@metalagman
Copy link

Issue is here: #4910

@SamMousa
Copy link
Collaborator Author

SamMousa commented Apr 3, 2018

The response object gets recreated but your bootstrap code is not run again; thus the event is not attached to the second response object...

The problem here is what to do for any request that's not the first in a test.

In your example not recreating the response object would make all requests in the test case return json.

I can fix it for the first request, but not sure about the n-th request; any idea?

@metalagman
Copy link

metalagman commented Apr 3, 2018

It's a common case to add a custom event handlers to the request/response components via configuration or via module bootstrapping. You need to persist them. As for me - keeping the app state between different tests is a bad idea. Different modules could attach different behaviors, event listeners, configurations to the app components. Keeping them persistent could break everything in a large apps.

@SamMousa
Copy link
Collaborator Author

SamMousa commented Apr 3, 2018

I agree, but I'm not talking about different tests but different requests inside test cases:

public function testA($I)
..
$I->doRequest('a');
//here you get the response object that results from the request. This must be recreated when the next line is called.
$I->doRequest('b');

The cause here is that there are multiple response objects and the second one won't have your bootstrapping code.

How is your bootstrap code bootstrapped? It would work if you bound the event to the class or to the application.

What would happen if any code creates a new response object? Whether bootstrapped or not, the assumption you make is that there's only one response object.

@SamMousa
Copy link
Collaborator Author

SamMousa commented Apr 3, 2018

We could of course store event handlers and reattach them when these 2 objects get recreated *-)

@metalagman
Copy link

We bootstrap the code is usual: in the bootstrap section of the config.
Like

'bootstrap' => [ 'api_module1', 'api_module2' ]

@SamMousa
Copy link
Collaborator Author

SamMousa commented Apr 3, 2018 via email

@metalagman
Copy link

Checked the code once again. We attach the event listeners Yii::$app->response->on(Response::EVENT_BEFORE_SEND, function ($event) {} in the init() of the modules. Not in the bootstrap($app).

@SamMousa
Copy link
Collaborator Author

SamMousa commented Apr 3, 2018

Okay, both should work though. Of course the problem is that module only gets initialized once.

Workaround would be to use application after request event. I'll post a fix later today

@bscheshirwork
Copy link
Contributor

@SamMousa how to configure mailer in single test now?

old helper

    public function configureMailer()
    {
        /** @var \Codeception\Module\Yii2 $module */
        $module = $this->getModule('Yii2');
        /** @var \yii\base\Application $app */
        $app = $module->app;
        /** @var \Codeception\Lib\Connector\Yii2 $connector */
        $connector = $module->client;
        $connector::$mailer = $app->get('mailer');
    }

old test case
https://github.com/bscheshirwork/yii2-app-advanced-rbac/blob/3ccfdae139bd508a9c5a06bb326326df07da172e/frontend/tests/functional/ContactFailCest.php#L13-L14

        \Yii::$app->set('mailer', 'frontend\stub\MockMailer');
        $I->configureMailer();

mock mailer: return fail on sending mail
php-code/frontend/tests/_stub/MockMailer.php
https://github.com/bscheshirwork/yii2-app-advanced-rbac/blob/3ccfdae139bd508a9c5a06bb326326df07da172e/frontend/tests/_stub/MockMailer.php#L6-L12

class MockMailer extends \Codeception\Lib\Connector\Yii2\TestMailer
{
    protected function sendMessage($message)
    {
        return false;
    }
}

@SamMousa
Copy link
Collaborator Author

SamMousa commented Apr 4, 2018

@bscheshirwork i'll get back to you

@SamMousa SamMousa deleted the yii2-rework2 branch April 4, 2018 16:04
@Slamdunk
Copy link
Contributor

Slamdunk commented May 3, 2018

@samdark @DavertMik please stop releasing BC breaks as bugfix. This PR completely changes the API of the involved classes and should have been tagged in 3.0.0.

Semver is not a joke, I had to drop each and every multiple range version constraint described in composer doc because the projects you maintain just ignore completely https://semver.org/

@SamMousa
Copy link
Collaborator Author

SamMousa commented May 3, 2018

@Slamdunk the issue here is that the public API was never clearly defined.
Therefore it is impossible to decide whether a change breaks BC, this would mean according to semver, every release should increase the major version number.
This would of course not help you at all other than that your default constraint might work better; you would still be stuck on an old version since there is no development going on in different major versions.

If you would like to contribute something useful instead of complaints feel free to create tests at https://github.com/codeception/yii2-tests

This way we can test changes to identify if they break BC before actually releasing something.

@Slamdunk
Copy link
Contributor

Slamdunk commented May 3, 2018

@Slamdunk the issue here is that the public API was never clearly defined.
Therefore it is impossible to decide whether a change breaks BC

API is always clearly defined: everything public and not signed as @internal is an API.
The fact that an API makes sense or not is another story, but Semver has no correlation to the value of an API.
If the major number doesn't change, every public methods/variable/constant shouldn't change.

this would mean according to semver, every release should increase the major version number.

Yeah, and the issue for that is?

This would of course not help you at all other than that your default constraint might work better; you would still be stuck on an old version since there is no development going on in different major versions.

That's the exact behaviour I expect from Semver.
If I want to stick to a specific API, it's my choice.

If you have not the resources to maintain multiple versions (as I don't have in the FOSS I maintain) it would be perfectly ok to bump frequently the major number.

If you would like to contribute something useful instead of complaints feel free to create tests at https://github.com/codeception/yii2-tests

This way we can test changes to identify if they break BC before actually releasing something.

Tests has no correlation to API (although are a fundamental step to decide where an API should be driven to); if you want to identify BC breaks there are simple tool like https://github.com/Roave/BackwardCompatibilityCheck to help you

@SamMousa
Copy link
Collaborator Author

SamMousa commented May 3, 2018

Does codeception even use semver at the moment?

@Slamdunk
Copy link
Contributor

Slamdunk commented May 3, 2018

The tag names suggest so.
If Codeception doesn't want to follow Semver that's OK, but if so it should immediately drop X.Y.Z tag template to avoid misleading informations.

@SamMousa
Copy link
Collaborator Author

SamMousa commented May 3, 2018

Lol, I think the X.Y.Z format has existed long before semver did.
As far as I know it is not a patented format.

@Slamdunk
Copy link
Contributor

Slamdunk commented May 3, 2018

Of course is not, but Codeception uses composer to spread itself, and it is statistically near 100 the percentage of the users that, in a composer ecosystem, would consider X.Y.Z to be a Semver-following package.

@SamMousa
Copy link
Collaborator Author

SamMousa commented May 3, 2018

Yet luckily there's very few that are so entitled as you are :)

@Naktibalda
Copy link
Member

Usually we try to avoid introducing breaking changes in patch releases.

@MysteryDragon
Copy link

MysteryDragon commented May 10, 2018

@SamMousa I just want to pay your attention on changed behavior which I faced few days ago.
If it's what you wanted to implement, that there are no problem (perhaps). (All right, it's disputable case, but still.)

So:
If we use haveFixtures() method directly, i.e. in _before() method, then for now database connections will not be closed after every tests.
There was issue: #3456
And this issue was solved: https://github.com/Codeception/Codeception/pull/3586/files
But for now you changed code, so only fixtures in _fixtures() method are carefully handled: 08e487b#diff-938f756d763fc21aace78bb43ea26870R292
And previous code was removed: 08e487b#diff-938f756d763fc21aace78bb43ea26870L242

Thanks for attention.

@czzplnm
Copy link

czzplnm commented Jun 4, 2018

Is this being worked on or is there a way for us to force close database connections?

@metalagman
Copy link

@czzplnm there is a quick workaround I've described in #4926

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants