Is singleton really antipattern in test automation?

10 minutes QA story
10 Minutes QA Story
6 min readMar 27, 2022

--

Welcome! Next 10 minutes we will try to decide if singleton pattern in your test automation project is anti-pattern or not. Also I’ll show you the examples of usage and implementation in specific cases.

Background

In modern world of software development singleton is considered as anti-pattern. You can find resources to explain why exactly, but in a nutshell the main reasons are the following:

  1. Singleton is generally used as a global instance. It leads to poor code design as you hide the dependencies of your application in code, instead of exposing them through the interfaces.
  2. Singleton is violating the single responsibility principle. they control their own creation and lifecycle.
  3. Singleton object is pretty hard to fake in tests and thus we can consider it as untestable code.
  4. Singleton keeps state for the lifetime of the application. So we cannot run several tests using this object at the same time, they will conflict with each other. It makes tests dependent and the theory of testing states that every unit test should be independent.

Hm… makes sense. But is the devil so black as he is painted (at least for test automation)? Let’s figure it out.

Options to implement singleton

#1 The simpliest one

The first and the simpliest option to implement a singleton class is just define static content inside it. You do not need to specify an object of this class to get access to data, you do not need to initialize this data — static classes are constructed during program startup. And the example of such singletone approach is a Config file storing application related constants.

public class Config {
public static final String login = "user";
public static final String password = "pass";
}

Of course, you can use various sources for filling this data — databases, env files, system variables etc.

You can see the usage of such class hopefully in every test automation project — URL of base page to open in UI tests, API url to make requests, constants for unit testing. But remember that a large amount of data to be loaded can take a long time to start. In such cases, you need to think about decomposition (see the singleton implementation selection algorithm below).

#2 Things get harder

What if we have a tricky logic on initializing objects in #1 section or we do not need the class right after application is loaded? We can write singleton object by different way, to get data from it “in a moment”, when we want get this data at runtime.

For example, we can have such object in UI tests where we defined the WebDriver object to be static and visible in any program class:

public class Driver {

private static WebDriver driver;
...

public static WebDriver get() {
if (driver == null) {
driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
}
return driver;
}
}

What is happening here is that we initializing the WebDriver instance for our tests and we can get right inside test or Page Object (depending on patterns you use for test automation). Note, that WebDriver starts only when we’re trying to access to it but not at program startup.

The only problem here is this code is not thread safe. If you need to run your tests in parallel, code inside if block can be done several times by differents threads. And, of course, we do not want to have one browser instance for all threads, so we need to modify our code.

#3 Thread safe option with one instance per thread

If we’re running tests in parallel then we have several threads and each of them should have own WebDriver instance. It’s pretty easy to implement actually:

public class Driver {

private static ThreadLocal<WebDriver> driver = new ThreadLocal<>();
...

public static WebDriver get() {
if (driver.get() == null) {
driver.set(new ChromeDriver(options));
}
return driver.get();
}
}

The difference with previously implemented option is that we use special Java class ThreadLocal . You have the similar classes to implement this type of singleton in any other programming language, just need 5 minutes of googling.

Now we have a separate instance of WebDriver for every thread and they won’t conflict with each other.

Config example examination

Okay, let’s try to examine our solution by 4 criteria described above.

#1 Global instance and hidden dependencies. All configuration data is in one place and the only hidden dependency might be the library to read data from configuration files (e.g. dotenv). There are no complex objects or classes. So I think we can mark this criterion as passed.

#2 Single responsibility principle. We have only one instance of configuration and use this class as a data storage. There is actually an extra responsibility inside — class initializing itself. But every static class initialize itself on program startup. Can we manage to exatract initializing into separate object? I think there should not be a problem here. My suggestion here is to use config class as a storage and if you need a complex mechnanism to load all data then just do it separately, no problem. But config class still remains a storage and no other responsibilities are noticed.

#3 Faking singltones. For what? To write a test? Is it okay to write tests for getters or class fields? Do we want to spend our time for testing basic functionality of programming language? I think no.

#4 Test independency. We have no tests on this class so this point is invalid.

It seems config file is okay for us, regardless it’s still a singletone.

WebDriver example examination

#1 Global instance and hidden dependencies. This is more complex. We really have global instance of WebDriver object, but in every thread regarding to ThreadLocal class. Is it okay for our tests? Yes, we have only 1 test running in thread at a time and this thread do not affect others. If you have several tests running in one thread we have an architectural problem then.

What about hidden dependencies? It’s true. We are using WebDriver static instance instead of pass a WebDriver object to every Page Object class. This approach is really implicit and may cause misunderstanding while debugging or reading the code. It also may leads to using WebDriver object in other classes than Page Objects. So this concern is valid.

#2 Single responsibility principle. We initialize our Driver in the same class we get it from. I get used to write this “getter” with initialization logic (if section in example). And in this case it seems like a constructor call (we call only one method get()). This approach allows us not to pass WebDriver object in every Page Object class and, what is the most important, to be sure we have initialized WebDriver while calling get() method. So yes, I can agree this is a mixing up of responsibilities, but the benefits…

#3 Faking singltones. This point is valid if you’re using one thread implementation. It’s really hard to fake WebDriver instance especially if you have several threads for unit testing. But have you ever seen unit tests for a test framework? If yes, I believe there were no tests about WebDriver object, but if I’m wrong please let me know, it’s very rare case. If we’re using ThreadLocal implementation then we have no such problem. My suggestion is to mark this concern as insignificant. So we have passed this check.

#4 Test independency. The reasons why I consider this one as passed are the same as in #3. It’s super rare case to write unit tests for WebDriver object and we still have separate threads for this static class.

Which approach to choose?

If you understand you have a task need to be implemented by singletone pattern but still have doubts about what solution to choose then you can use the algorithm I’ve prepared to you. But feel free to learn more techniques described by link below.

Conclusions and next steps

I was trying to explain why I do not consider singleton as anti pattern in test automation area. And I’ve shown you this point by examples. We used only three implementation for this pattern but there are many more existent. I still think that singleton should be avoided if you’re a developer and write code actually. It leads to complex dependency management and makes your code unreadable and untestable. But in test automation area singletone gives us cool benefits.

Are you thinking differently? Welcome to comments then.

I also wanted to post a link on singleton pattern here to give you an ability to completely learn this pattern. Here it is: https://www.geeksforgeeks.org/singleton-design-pattern

If you like my articles please subscribe to my telegram channel where we have more useful links for QA engineers and others.

Good luck and don’t stop to automate!

--

--

10 minutes QA story
10 Minutes QA Story

Welcome everyone! I am a QA Engineer with manual testinsg, automated testing and team lead experience. Let’s do the test stuff here!