Giter Site home page Giter Site logo

Comments (20)

inxilpro avatar inxilpro commented on May 11, 2024 8

Another option until my PR is approved. Add this to your root DuskTestCase:

public function browse(Closure $callback)
{
    parent::browse($callback);
    static::$browsers->first()->driver->manage()->deleteAllCookies();
}

from dusk.

JVMartin avatar JVMartin commented on May 11, 2024 6

@georaldc Just wanna make sure you didn't miss that I gave code above to circumvent the problems without using database driver for session/cache.

Also, for way faster testing, you can simply run your migrations and seeds once beforehand, creating a prepared.sqlite file and then use it as a fresh copy of your database on every test so you don't have to keep re-running your migrations and seeds.

This ends up looking like this (in tests/DuskTestCase.php):

/**
 * Set up our testing environment
 *
 * @return void
 */
public function setUp()
{
	parent::setUp();

	// Reset the sqlite testing database
	// http://www.chrisduell.com/blog/development/speeding-up-unit-tests-in-php/
	copy(database_path('prepared.sqlite'), database_path('database.sqlite'));

	// Reset the cache and sessions.
	exec('git clean -fxd ' . storage_path('framework/cache'));
	exec('git clean -fxd ' . storage_path('framework/sessions'));
}

from dusk.

inxilpro avatar inxilpro commented on May 11, 2024 6

A quick fix:

Inside your browse() closure, run:

$browser->driver->manage()->deleteAllCookies();

I'm going to submit a PR for this as well.

from dusk.

JVMartin avatar JVMartin commented on May 11, 2024 3

As @georaldc pointed out, the issues with the new architecture are deep, and prevent all manner of integration testing that used to be possible.

For instance:

public function testForgotPasswordFlow()
{
	Mail::fake();

	// Use the forgot password form.
	$this->browse(function(Browser $browser) use ($user) {
		$browser->visit(new HomePage)
			->clickLink('forgot password?')
			->type('@forgot-password-email', '[email protected]')
			->press('Email Me')
			->waitForText(trans('auth.forgot-pass'));
	});

	Mail::assertSent(ResetPasswordLinkEmail::class);
}

The assertSent() will always fail. This kind of testing no longer works because the application itself is now completely decoupled from the testbed.

How are we supposed to do integration tests now?

from dusk.

JVMartin avatar JVMartin commented on May 11, 2024 1

This is an issue for me as well. The session and the cache persist from test to test, and there's no way to use "array" for either. So all those files need to be deleted between every test.

I'm currently achieving this by setting them both to "file" and then using the following in my DuskTestCase.php:

/**
 * Set up our testing environment
 *
 * @return void
 */
public function setUp()
{
	parent::setUp();
	exec('git clean -fxd ' . storage_path());
}

(This deletes all the session and cache files.)

from dusk.

georaldc avatar georaldc commented on May 11, 2024 1

@JVMartin yup, saw that. Was just giving an alternative solution. Running migrations happen very quickly for me so I am not too concerned about it affecting speed (maybe if I have a ton of tests would I look for a different approach, such as your suggestion so thanks for that). I have bigger speed related problems though when using dusk, which can be found in a separate issue I created a few days ago - #98

from dusk.

fschwaiger avatar fschwaiger commented on May 11, 2024 1

I agree! I just moved sessions to the database and this is a nice workaround. The tests pass immediately.

> php artisan session:table
> php artisan migrate

Edit .env.dusk:

SESSION_DRIVER=database

EDIT: Note this only works if you use DatabaseMigrations.
To improve, you can edit the migration file to only create the sessions table in testingenvironment.

from dusk.

MoogyG avatar MoogyG commented on May 11, 2024 1

Same things here, I suspect $browser->driver->manage()->deleteAllCookies(); to be asynchronous, it could explain these random results.
The Curl call of facebook driver in /vendor/facebook/webdriver/lib/Remote/HttpCommandExecutor.php is synchronous but maybe Chrome is queuing the task so the next Dusk test could be running before cookie deletion.

I didn't find a universal solution to handle all sessions drivers, you have to make your own.

For me I had to change SESSION_DRIVER from redis to file and use exec('git clean -fxd ' . storage_path('framework/sessions'));

It would be great to have an artisan command to delete all sessions, regardless of session driver.

from dusk.

Sieabah avatar Sieabah commented on May 11, 2024

You should drop the session each test instead of killing the entire chromedriver process. Plus, why aren't you mocking the session in your tests? Or set the session to Array

from dusk.

fschwaiger avatar fschwaiger commented on May 11, 2024

Mocking the session is not possible since the browser is a different process.
Ensuring the test environment is clean should be the job of browse(...), setUp() or tearDown().

from dusk.

georaldc avatar georaldc commented on May 11, 2024

@Sieabah You can't use array because as already mentioned, the actual tests execute under a different process. Probably the same reason you cannot use in-memory sqlite. One way to circumvent these problems is to use a physical sqlite file (if you want to use sqlite), set both cache and session drivers to use the database and use the DatabaseMigrations trait to continually reset your database before every test. Of course, this means you would need the migration files for both cache and session tables, even if you will be using something else on a different environment like production.

Because of this though (running on a separate process), I just realized that this prevents me from setting up my application environment such as mocking services and disabling middleware since anything that happens in my DuskTestCase classes won't exist during the actual test runs. Any way around this?

from dusk.

SebastianS90 avatar SebastianS90 commented on May 11, 2024

@JVMartin regarding mails you could configure laravel to submit mails to mailtrap, then use their API to verify that the mail has been sent when executing your Dusk tests. There is this great inboxes/{inbox_id}/messages endpoint that lists you all mails in your inbox.

But I guess the more Laravel way would be to implement what I've sketched in #152. That would also be a lot faster than sending actual mail over the network to mailtrap and pulling the data back into your test case for verification. And you could make it work for anything where you can set the driver in the .env file.

from dusk.

mtmail avatar mtmail commented on May 11, 2024

Another work-around is using https://github.com/themsaid/laravel-mail-preview and then fetching the HTTP from there. Of course don't enable it on production systems. Drawbacks: hard to parse the subjects line (it's inside a HTML comment) and you only have access to one (the last) email. It saves the emails to a text file, similar to the Dusk screenshots.

from dusk.

deleugpn avatar deleugpn commented on May 11, 2024

I found this topic while searching about Laravel Session and it worried me a little because I was unable to reproduce this problem. This is what I had as my test:

	/**
	 * @test
	 * @return void
	 */
	public function it_should_login() {
		$user = factory(Admin::class)->create();
		$this->browse(function (Browser $browser) use ($user) {
			$browser->visit('/')
				->type('email', $user->email)
				->type('password', $this->password)
				->press('Login')
				->assertSee(__('Dashboard'));
		});
	}
	
	/**
	 * @test
	 * @return void
	 */
	public function it_should_see_email_error_message() {
		$this->browse(function (Browser $browser) {
			$browser->visit('/')
				->press('Login')
				->assertSee(__('The email field is required.'))
				->assertSee(__('The password field is required.'));
		});
	}

So I thought: why am I not facing this issue? The only thing that came to mind was the following:

// Start the database from database.sql
$mysql = 'mysql -D' . getenv('DB_DATABASE') . ' < ' . __DIR__ . '/../database/database.sql';
exec($mysql);

Each test will have a clean database because of these lines that I have on createApplication, which means I was only unable to reproduce this problem because when it_should_see_email_error_message is called, there is no user with id 1 inside the database (because it was created from scratch again). If I just add $user = factory(Admin::class)->create(); to the begin of the test, it is enough to cause it to fail because then the user with id 1 that is considered logged can be found in the database.

from dusk.

sileence avatar sileence commented on May 11, 2024

@fschwaiger that seems to be a very clever solution, thank you!

from dusk.

SebastianS90 avatar SebastianS90 commented on May 11, 2024

How about Laravel Passport that uses JWT cookies instead of sessions?

from dusk.

 avatar commented on May 11, 2024

For localStorage tokens like those you might set using a JWT solution, you could do the following

    $this->browse(function (Browser $browser) use ($admin) {
        $browser->script("localStorage.clear()");
        $browser->visit('/dash')
            ->on(new DashboardLoginPage)
            ->assertPathIs('/dash/login');
    });

You can execute scripts on the browser using $browser->script() in this case i'm using JS to clear the localStorage before executing a test that requires the user not be signed in.
I was using $this->closeAll() before but this is much much faster.

from dusk.

Jamesking56 avatar Jamesking56 commented on May 11, 2024

@smtlk That localStorage solution doesn't work for me, I get an error:

Facebook\WebDriver\Exception\NoScriptResultException: <unknown>: Failed to read the 'localStorage' property from 'Window': Storage is disabled inside 'data:' URLs.

from dusk.

driesvints avatar driesvints commented on May 11, 2024

Closing this issue because it's already solved, old or not relevant anymore. Feel free to reply if you're still experiencing this issue.

from dusk.

ryancwalsh avatar ryancwalsh commented on May 11, 2024

@inxilpro I appreciate the deleteAllCookies() suggestion, but it hasn't worked. https://stackoverflow.com/a/49451157/470749 worked for a while on certain tests, but on tests I'm writing today, according to Dusk screenshots in my tests, my Laravel login session continues even after calling foreach (static::$browsers as $browser) { $browser->driver->manage()->deleteAllCookies(); }. It's so bizarre!

from dusk.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.