liuggio / fastest

Simple parallel testing execution... with some goodies for functional tests.
MIT License
473 stars 65 forks source link

Problem with setting header in symfony #125

Closed mfratczak closed 5 years ago

mfratczak commented 6 years ago

Hi,

I have use opensoure sylius ecommerce system, and i want use fastest. But we use selenium or local server to test (using behat). How i can put header or cookie on start to every test? i dont want change every test. I'm trying this more than 4 days, and still have problem :)

DonCallisto commented 6 years ago

@mfratczak fastes just parallelize them: if you set header and/or cookie in the "standard" way (e.g.: for behat in a @BeforeScenario) all should work as expected. I use fastest with behat and selenium and I don't have any kind of issue.

OskarStark commented 6 years ago

@DonCallisto can you please share your snippet of the BeforeScenario method and how you implement the different database names?

DonCallisto commented 6 years ago

@mfratczak, @OskarStark we do something like

/**
  * @BeforeScenario
  */
public function before(BeforeScenarioScope $scope)
{
  [...]
  if (getenv('ENV_TEST_CHANNEL_READABLE')) {
    $this->getSession()->setCookie('ENV_TEST_CHANNEL_READABLE', getenv('ENV_TEST_CHANNEL_READABLE'));
  [...]
}
OskarStark commented 6 years ago

Nice an are you using mysql or SQLite for test? And how do you replace the dB name then? Using sf 4 so have a DATABASE_URL which will be resolved

OskarStark commented 6 years ago

@liuggio did you think about a behat context which could be enabled and provide the cookie stuff by @DonCallisto ?

DonCallisto commented 6 years ago

@OskarStark Right now we're using SQLite for tests and MySQL for real env. I know, it kinda sucks because we cannot reproduce the real stack but ATM we never had troubles with this. As our infrastructure is huge we never switched but we should sooner or later. Talking about our solution, we create "statically" a db that we copy (as sqlite dbs are files) with right db name as fastest requires. This way we can get rid of that copy of db as soon as a new test starts. In MySQL a suitable solution would be to create n db in advance and use, for example, ORMPurger: I have this solution for project where I don't use fastest and it simply works so I can't see why it shouldn't with multiple db as long as process are isolated (as it is in fastest).

BTW @liuggio don't follow this project anymore AFAIK and I really don't think this kind of Context should be something that fastest should care about. TBH all behat things should live into an extension but for the moment that's not a big deal for the project to keep them internal. Moreover that's just a basic configuration, I don't know if it's worth to create "ad hoc" context just for this.

WDYT?

OskarStark commented 6 years ago

Hey @DonCallisto thank your for your feedback.

I try something like this with Symfony 4.1:

// The check is to ensure we don't use .env in production
if (!isset($_SERVER['APP_ENV'])) {
    if (!class_exists(Dotenv::class)) {
        throw new \RuntimeException('APP_ENV environment variable is not defined. You need to define environment variables for configuration or add "symfony/dotenv" as a Composer dependency to load variables from a .env file.');
    }
    (new Dotenv())->load(__DIR__.'/../.env');
}

FastestEnvironment::setFromRequest();
$testDatabaseUrl = str_replace('luna', getenv('ENV_TEST_CHANNEL_READABLE'), getenv('DATABASE_URL'));

putenv("DATABASE_URL=$testDatabaseUrl");

but getenv('ENV_TEST_CHANNEL_READABLE') is empty.

I execute it like this: vendor/bin/behat --list-scenarios | ./vendor/liuggio/fastest/fastest "vendor/bin/behat {}

And added this to my FeatureContext:

    /**
     * @BeforeScenario
     */
    public function addFastestChannelInformation()
    {
        if (getenv('ENV_TEST_CHANNEL_READABLE')) {
            $this->minkContext->getSession()->setCookie('ENV_TEST_CHANNEL_READABLE', getenv('ENV_TEST_CHANNEL_READABLE'));
        }
    }

Regarding the context, I think we could make it quite simple for people to start by registering the context, of read this issue 😄

Creating n MySQL db's shouldn't be a Problem but I need to select them anyway. Can you elaborate or show some more detail on your implementation? Maybe a gist or sth like this?

DonCallisto commented 6 years ago

Well, I have a quite complex setup as we use doctrine sharding so this is little bit more complicated and right now I can't take a look and get rid in a safe way of all those concept and post something that surely work (as I should take time to get rid of all sharding concepts and then test that's fine). BTW that's nothing complex here: before every scenario I retrieve the ENV_TEST_CHANEL_READABLE var, and do a cp (remember, I'm on sqlite) of the "base" db (that I set in the script I use to launch the tests) into a new one with ENV_TEST_CHANEL_READABLE appended in the name. That's it as fastest will do the rest for me as db selection happens directly in fastest as described here

I suppose that

but I need to select them anyway

is resolved with the link I've provided to you

OskarStark commented 6 years ago

Ah i overlooked that, but link, thank you!

But now I got this error: [2018-06-21 06:12:35] php.DEBUG: Notice: Undefined index: master {"exception":"[object] (Symfony\Component\Debug\Exception\SilencedErrorContext: {\"severity\":8,\"file\":\"/var/www/vendor/liuggio/fastest/adapters/Doctrine/DBAL/ConnectionFactory.php\",\"line\":30,\"trace\":[{\"file\":\"/var/www/var/cache/test/ContainerF7lD3eR/getDoctrine_Dbal_DefaultConnectionService.php\",\"line\":12,\"function\":\"createConnection\",\"class\":\"Liuggio\\Fastest\\Doctrine\\DBAL\\ConnectionFactory\",\"type\":\"->\"},{\"file\":\"/var/www/var/cache/test/ContainerF7lD3eR/srcTestDebugProjectContainer.php\",\"line\":838,\"args\":[\"/var/www/var/cache/test/ContainerF7lD3eR/getDoctrine_Dbal_DefaultConnectionService.php\"],\"function\":\"require\"}],\"count\":1})"} []

are you sure this works with Sf4 ?

this is the failing code in ConnectionFactory:

    public function createConnection(array $params, Configuration $config = null, EventManager $eventManager = null, array $mappingTypes = [])
    {
        if (isset($params['dbname'])) {
            $dbName = $this->getDbNameFromEnv($params['dbname']);
        } else {
            $dbName = $this->getDbNameFromEnv($params['master']['dbname']);
        }
DonCallisto commented 6 years ago

Not sure that's a symfony problem as those data should came from DBAL, as this connection is used by that layer.

OskarStark commented 6 years ago

I found an existing issue by @tarlepp : #101

DonCallisto commented 6 years ago

However this project should have nothing to do with symfony (apart for direct deps used in the code), maybe we should write a kind of "normalizer" to be sure to accept those parameters in the same format regardless how they're declared inside config files.

tarlepp commented 6 years ago

@OskarStark I resolved this with custom bootstrap with phpunit and a small configuration change:

doctrine:
    dbal:
        url: '%env(DATABASE_URL)%'
        # This is needed for 'fastest' see https://github.com/liuggio/fastest/issues/101
        dbname: '%env(DATABASE_NAME)'
###> doctrine/doctrine-bundle ###
# Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"
# Set "serverVersion" to your server version to avoid edge-case exceptions and extra database calls
DATABASE_NAME=symfony
DATABASE_URL=mysql://root@127.0.0.1:3306/${DATABASE_NAME}?charset=utf8mb4&serverVersion=mariadb-10.2.14
###< doctrine/doctrine-bundle ###
OskarStark commented 6 years ago

@tarlepp thank your for your feedback, I got it almost working 🙏

An exception occurred in driver: SQLSTATE[HY000] [1049] Unknown database 'luna_test_6' (Doctrine\DBAL\Exception\ConnectionException)

the name is routed through the test scope, but the database is not created. How do you to this or should this be done automatically?

OskarStark commented 6 years ago

the name is routed through the test scope, but the database is not created. How do you to this or should this be done automatically?

Ah I found this in your repo: https://github.com/tarlepp/symfony-flex-backend/blob/master/tests/bootstrap.php

Is this everything I need todo?

tarlepp commented 6 years ago

@OskarStark that is how I got that working, there might be some parts you don't need though. One important thing is also in public/index.php to make fastest environment working with those client tests.

OskarStark commented 6 years ago

I got it working and here are my behat contexts I use.

https://gist.github.com/OskarStark/5a3ce2bab27998a32a458377650abaa2

@tarlepp instead of executing the database commands via doctrine, fastest supports: -b, --before=BEFORE Execute a process before consuming the queue, it executes this command once per process, useful for init schema and load fixtures.

I will try to get it working, but not sure if this is executed in the bootsraped environment 🤔 Did you tried that already?

OskarStark commented 6 years ago

The funny thing is, that my suite takes 5:04 minutes with fastest and 4:51 minutes without fastest

84 scenarios (82 passed, 2 undefined)
619 steps (605 passed, 2 undefined, 12 skipped)
4m51.53s (99.44Mb)
DonCallisto commented 6 years ago

How many process did you use for consumption?

OskarStark commented 6 years ago
- 84 shuffled test classes into the queue.
- Will be consumed by 8 parallel Processes.

I am on a MacBook Pro (15', 2016) using Docker-Compose: screenshot 2018-06-21 10 37 27

DonCallisto commented 6 years ago

That's weird ...

tarlepp commented 6 years ago

weird with that speed, in my case I get following; without fastest Time: 2.54 minutes and with fastest Time: 59 seconds 559 milliseconds

But in earlier I noticed that fastest will impact e2e more than just simple pure unit tests.

seyfer commented 5 years ago

@DonCallisto I'm getting the error

┌─ @BeforeScenario # FeatureContext::before()
  │
  │  test_1
  │
  ╳  Exception: unable to set cookie
  ╳    (Session info: chrome=71.0.3578.80)
  ╳    (Driver info: chromedriver=2.44.609551 (5d576e9a44fe4c5b6a07e568f1ebc753f1214634),platform=Linux 4.15.0-43-generic x86_64) in vendor/instaclick/php-webdriver/lib/WebDriver/Exception.php:144
  ╳  Stack trace:

with the code

    /**
     * @BeforeScenario
     */
    public function before()
    {
        if (getenv('ENV_TEST_CHANNEL_READABLE') && $this->getSession()->isStarted()) {
            echo getenv('ENV_TEST_CHANNEL_READABLE');
            $this->getSession()->setCookie('ENV_TEST_CHANNEL_READABLE', getenv('ENV_TEST_CHANNEL_READABLE'));
        }
    }

have no idea why. any suggestions?

also, I have tried to set header with the code

//        if (getenv('ENV_TEST_CHANNEL_READABLE') && $this->getSession()->isStarted()) {
//            $cookie = new Cookie('ENV_TEST_CHANNEL_READABLE', getenv('ENV_TEST_CHANNEL_READABLE'));
//            $this->getSession()->setRequestHeader('Cookie', (string)$cookie);
//        }

and it gives me error

┌─ @BeforeScenario # FeatureContext::before()
  │
  ╳  Behat\Mink\Exception\UnsupportedDriverActionException: Request headers manipulation is not supported by Behat\Mink\Driver\Selenium2Driver in vendor/behat/mink/src/Driver/CoreDriver.php:281

Symfony 3.4. latest Behat and Mink.

seyfer commented 5 years ago

Or any idea how to attach GET parameter to all requests with Symfony Behat Mink? I know they have guzzle parameters configurable...

DonCallisto commented 5 years ago

@seyfer that's not a fastest issue, btw you can search for a solution on the web or open a issue at Selenium repository.

AFAIK, before setting a cookie you should start navigate at least one page of the domain. For example, before that set, we do something like

$this->visitPath('/?XDEBUG_SESSION_START=IDE-DEBUG-KEY');

That is not related to fastest but is useful when you need to set a debug key (and I suppose that consequently we are able to set the cookie).

seyfer commented 5 years ago

@OskarStark

The funny thing is, that my suite takes 5:04 minutes with fastest and 4:51 minutes without fastest

Do you run Selenium in Hub mode with several browser instances? If not, it gives no benefit as processes wait for one browser instance to be available.

DonCallisto commented 5 years ago

I'm closing it as it's not a fastest issue and the converstation seems to be over.