Ensuring your tests are compatible with Parallelisation

  • 10 March 2021
  • 0 replies
  • 8 views

Userlevel 1
Badge +2

Parallelisation has to be intentional.

I feel like parallelisation is one of those things which people would really like there to be a simple solution to… And there isn’t one.  Parallelisation is like quality; it’s much easier to achieve if it’s designed in from the start, rather than applied at the end like buttercream on an ugly cake.

 

How I design for parallelism

I’ll admit, I’m not perfect when it comes to making sure my tests are parallel-safe, but I do have a few techniques I use from the start of a new test suite.

User Generation

I don’t use pre-made test users.  I think it’s a test smell, honestly.  I always want to create a new user, with a new, random username.  In fact, I want one for each concurrent test I’ll be running.  That way, I know my tests against one thread won’t step on tests for another; My users are totally separate.

(I also don’t create users via the “new user” pages, unless those are what I’m testing.  I directly edit the user database instead; It’s faster.)

Data Generation

I also try not to use pre-made test data.  Instead, I like to generate data schemas.  

Definition of schema

1a diagrammatic presentation broadly a structured framework or plan OUTLINE

(From Merriam-Webster)

That is, I create a factory that can generate valid test data and inject it directly into the application.  For example, in Ruby projects I like to use factory_bot and FFaker to generate example data.  I can do things like:

# This will create an account entry
FactoryBot.define do
factory :account_entry do
creation_datetime { FFaker::Time.between(Time.now-7, Time.now }
amount { "#{FFaker::Currency.unit} #{rand(100)}.#{rand(99)}" }
vendor { FFaker::Company.name }
end
end

Every time I create an account entry, it will be given a random creation date & time from the past week, a random amount between 0 and 100.99, and a random company name.

By generating data randomly, not only am I saving time, I’m also sure that my tests don’t depend on other tests to create data, and I’m adding a small amount of uncertainty into my tests, giving me a greater chance of uncovering bugs.

(There are similar tools in other languages; Check out JavaFaker for Java or Rosie for JS).

Service Thread Safety

If I’m using a 3rd party service, I try to make sure that both it and the code interacting with it is thread safe.

For instance, if there’s a legacy system that’s only able to process one request at a time, I’ll create a shared queue for my tests.  That moves the responsibility from interacting with that system safely out of my tests; As long as they’re happy to wait for their response, they can simply make requests of that queue and sit there until the legacy system does… whatever.

Selenium Thread Safety

This is the big one.  I make sure that, however my Selenium session is being created, it’s done in a way that ensures each parallel test gets a unique driver that can’t be accessed from other tests.

For example; When using TestNG, the test class is shared by every thread.  So, if you create a Driver class variable and instantiate it in BeforeMethod, it can get over-written by another thread.  Not good.

To stop that happening, I use things like <ThreadLocal> to make sure that each thread has it’s own, unique Driver.

 

How do you do it?

I’d like to hear from everyone how they get their applications and tests ready for parallelisation.  What do you do from the beginning, and how do you fix things if you’re trying to add parallelism after the fact?


0 replies

Be the first to reply!

Reply