Multithreading with Playwright (Java)

Adhikariparul
helpshift-engineering
4 min readMay 23, 2022
“Multitasking Cartoon Man With Original Background” is licensed under CC BY 1.0

Have you ever wondered how it would have been like if a web browser like Google Chrome ran on a single thread? The system would have become completely unresponsive with tasks as simple as opening new tabs or closing the browser getting queued up, had a page load been initiated.

In comes Multithreading to the rescue!

Java provides built-in support for multithreading. It enables parallel execution of two or more parts of a program, each part being a thread.

Playwright

Playwright Java in itself is not thread-safe i.e. each of Playwright, BrowserType, Browser, BrowserContext and Page objects are expected to be used in the same thread. But there is a possibility to create multiple Playwright instances, each on its own thread.

Let’s see how we’ve implemented multithreading in our Playwright framework.

We’ve created a PlaywrightFactory.java class, the primary job of which is to handle creation and fetching of any active Playwright instances, as well as all objects created by it.

  1. Create thread safe variables of type Playwright, BrowserType, Browser, BrowserContext and Page:

2. Define a method to decide whether to create a Page instance or return an active instance of it:

3. Define a method to handle Page creation:

4. Define a method to handle closing of Browser:

How do you put all of this into action?

Note how the only public method in the PlaywrightFactory class that we created is getPage(). All you have to do is, at the start of every @Test method, make a call to this method. Basis the thread groups that you run your tests with, it will automatically decide whether to create a new browser context for that group or use an active thread for it.

That is it! Once you set up all of this in your framework, you’ll have an automation suite running tests in parallel. :)

But wait! Before you do that, make sure that any function call that has a possibility of being accessed by two threads at the same time is thread-safe. How do you do that?

Using synchronization.

Synchronization makes sure that a resource, being accessed by multiple threads at the same time, is allowed access to a thread only once the other thread releases its lock on it. For instance:-

Multithreaded run

Now that we have a thread-safe multithreaded framework in place, let’s see how it’s execution time differs from that of a single threaded run:-

Multi-threaded run
Single-threaded run

You’d notice that as the test coverage increases and on the basis of how tests are grouped when run in parallel, the time difference would get more prominent.

Typically the features you are grouping together must be as independent as possible.

While synchronisation comes into play, it also means that a thread acquiring a lock on an object, would release it for another thread wanting access to a shared resource only when the first thread completes its tasks. So that ends up adding to the total execution time. Sometimes this kind of grouping is doable, sometimes it’s not. For the times its not, its still okay to be thread-safe than have inconsistent results. :)

Happy testing, folks!

Conclusion

While integration testing involves testing how independent logical entities in an app interact with each other, multithreaded execution tests specific functionalities of a thread. By following best practices, you can not only build a better multithreaded approach to testing, but also optimize resources while significantly reducing testing time.

--

--