How to handle(not solve) captcha that is sometimes added to the form after its submission in E2E tests?

Viewed 93

In the system that I'm testing captcha appears very rarely, but when it does, it's out of the blue(I can't guess when) after I submit the form. Form data is preserved, user just needs to solve the captcha(solving is not an issue in my case). The problem is that I don't know when the captcha will appear and just blindly looking for each in the [AfterStep] hook is shot in the foot as it will block each step for implicit wait time.

My solutions are:

  1. After each step where I confirm a form I add a step And Fill captcha if necessary

This is bad because I add 50 steps to the whole test suite and 49 out of them are usually useless + Driver.FindElement is a blocking call so it will wait 49*20seconds for the captcha that will never appear.

  1. Browser gets HTTP code X when the captcha will appear after form submission so I could listen to the codes in the separate thread and once I hit code X I will look for captcha, fill and resubmit form.

This is bad because I found endless discussions stating that Selenium doesn't support it and I can't listen status codes, I don't think that has changed in recent times.

I will probably go with the 1. solution as this is the only one I know that will work, but I might be missing something.

Question: Are there any other solutions to this problem?

1 Answers

You need a class that specializes in detecting and handling the captcha, and then delegate to that class every time you interact with the page:

class CaptchaHandler
{
    readonly IWebDriver driver;

    private bool IsCaptchaVisible
    {
        get
        {
            // detect if captcha is visible
        }
    }

    internal CaptchaHandler(IWebDriver driver)
    {
        this.driver = driver;
    }

    internal void SolveCaptchaIfVisible(Action action)
    {
        try
        {
            action();
        }
        catch (WebDriverException)
        {
            if (IsCaptchaVisible)
            {
                SolveCaptcha();
                action();
            }
            else
            {
                throw;
            }
        }
    }

    private bool SolveCaptcha()
    {
        // solve the captcha
    }
}

Just call SolveCaptchaIfVisible each time you need to fill out the captcha, and pass it a lambda expression:

var handler = new CaptchaHandler(driver);

handler.SolveCaptchaIfVisible(() =>
{
    // Do something that might fail because captcha is blocking
});

This also gives you one place to optimize how the captcha is detected.

If there are certain steps that seem to trigger the captcha, initialize and use this class right in the step definition. The nice thing about this approach is you can reuse it if you need to write selenium tests in plain C#, without the Gherkin layer in between.

Related