Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Debugging and Taking Screenshots with JavaScript

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

What about debugging in JavaScript? Change your scenario so it fails: change the "Name" field to look for a non-existent "Product Name":

... lines 1 - 20
Scenario: Add a new product
... lines 22 - 25
And I fill in "Product Name" with "Veloci-chew toy"
... lines 27 - 34

Run it:

./vendor/bin/behat features/product_admin.feature:20

Here's the problem: you can try to watch the browser, but it happens so quickly that it's hard to see what went wrong. In the terminal, the error tells us that there isn't a field called "Product Name", but with nothing else to help. Was there an error on the page? Are we on the wrong page? Is the field calling something else? Why won't someone tell us what's going on!?

Let me show you the master debugging tool. Google for behatch contexts. This is an open source library that has a bunch of useful contexts - classes like FeatureContext and MinkContext with free definitions. For example, this has a BrowserContext you could bring into your project to gain a bunch of useful definitions.

Pausing Selenium

I don't use this library directly, but I do steal from it. The DebugContext class has one of my favorite definitions: iPutABreakPoint(). Copy that and drop it into our FeatureContext file:

... lines 1 - 140
/**
* Pauses the scenario until the user presses a key. Useful when debugging a scenario.
*
* @Then (I )break
*/
public function iPutABreakpoint()
{
fwrite(STDOUT, "\033[s \033[93m[Breakpoint] Press \033[1;93m[RETURN]\033[0;93m to continue...\033[0m");
while (fgets(STDIN, 1024) == '') {}
fwrite(STDOUT, "\033[u");
return;
}
... lines 153 - 188

Or you could even create your own DebugContext if you wanted to organize things a bit. Shorten this to "I break". To use this, add this language directly above the "Product Name" step that's failing:

... lines 1 - 20
Scenario: Add a new product
... lines 22 - 25
And break
And I fill in "Product Name" with "Veloci-chew toy"
... lines 28 - 35

The "I" part of this language is optional. Head back to the terminal to try this:

./vendor/bin/behat features/product_admin.feature:20

This time, the modal pops open, and the browser freezes. The terminal just says: "Press [RETURN] to continue..." That's right: it's waiting for us to look at the page and debug the issue. Once we know what the problem is, hit enter to let it finish. This is my favorite way to debug!

Taking Screenshots

But there are more cool things, like iSaveAScreenshotIn(). Copy that definition and paste it into FeatureContext. Change the language to "I save a screenshot to" and remove $this->screenshotDir thing since we don't have that. To save screenshots to the root of your project, replace it with __DIR__'/../../':

... lines 1 - 153
/**
* Saving a screenshot
*
* @When I save a screenshot to :filename
*/
public function iSaveAScreenshotIn($filename)
{
sleep(1);
$this->saveScreenshot($filename, __DIR__.'/../..');
}
... lines 164 - 199

In the scenario add:

... lines 1 - 20
Scenario: Add a new product
... lines 22 - 25
And I save a screenshot to "shot.png"
And I fill in "Product Name" with "Veloci-chew toy"
... lines 28 - 35

Run it!

./vendor/bin/behat features/product_admin.feature:20

The modal opens and it still fails at the "New Product" step. But now we have a fancy new shot.png file at the root of the project that shows exactly what things looked like when the test failed. Woah.

Saving Screenshots on Failure

If you're using continuous integration to run your tests - which you should be! - this can help you figure out why a test failed, which is normally pretty hard to debug. By using the hook system - something like @AfterScenario - you could automatically save a screenshot on every failure. Check out our blog post about that: Behat on CircleCI with Failure Screenshots.

Anyways, remove this line and change "Product Name" back to "Name" so that the scenario passes again:

... lines 1 - 20
Scenario: Add a new product
... lines 22 - 25
And I fill in "Name" with "Veloci-chew toy"
... lines 27 - 34

Leave a comment!

4
Login or Register to join the conversation
Soltan Avatar

Hi all,

What if 2 test fails, how can we take 2 or more screenshots from different feature files?
Thank you.

Reply

Hey Murad,

You just need to configure taking screenshots on failures, see https://symfonycasts.com/sc... . And of course, use different file names to avoid overriding screenshots.

Cheers!

Reply
Soltan Avatar

We are using Jenkins for CI, how can I configure taking screenshots considering Jenkins instead of CircleCI?

Thank you

Reply

The logic should be the same but you may have to tweak the code in order to satisfy Jenkins needs

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial uses a very old version of Symfony. The fundamentals of Behat are still valid, but integration with Symfony will be different.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.4.0, <7.3.0",
        "symfony/symfony": "^2.7", // v2.7.4
        "twig/twig": "^1.22", // v1.22.1
        "sensio/framework-extra-bundle": "^3.0", // v3.0.16
        "doctrine/doctrine-bundle": "^1.5", // v1.5.1
        "doctrine/orm": "^2.5", // v2.5.1
        "doctrine/doctrine-fixtures-bundle": "^2.2", // v2.2.1
        "behat/symfony2-extension": "^2.0" // v2.0.0
    },
    "require-dev": {
        "behat/mink-extension": "^2.0", // v2.0.1
        "behat/mink-goutte-driver": "^1.1", // v1.1.0
        "behat/mink-selenium2-driver": "^1.2", // v1.2.0
        "phpunit/phpunit": "^4.8" // 4.8.18
    }
}
userVoice