MaxNevermind
MaxNevermind

Reputation: 2923

Are there any potential issues with replacing Singletons with Classes?

I'm currently working on a Scala project and faced difficulties testing a code which uses Singleton objects extensively.

I have Singletons like this:

object SomeSingleton {
  def someFunction(s: String): String = s + "b"
}

And somewhere in a project I have it's usage in a method I want to test:

  val s = SomeSingleton.someFunction("a")

I develop new features and I want SomeSingleton.someFunction to return some specific value in tests without a real method call, I did this in Java using Powermock but I failed finding a way to do this in Scala. I have an idea how I can still do this but it implies changing the original code to this:

object SomeSingleton extends SomeSingletonClass{}

class SomeSingletonClass {
  // Whole logic from SomeSingleton moves here
  def someFunction(s: String): String = s + "b"
}

Now I can replace usage of SomeSingleton with SomeSingletonClass and properly mock it. So here are my concerns, the project is not small, it's not 100% test covered and I'm relatively new to Scala, so I'm not sure, are there any bad consequences it can lead to? Is it a viable solution? I don't want my legacy code to break in some random place because of this.

Upvotes: 1

Views: 58

Answers (1)

Dat Nguyen
Dat Nguyen

Reputation: 1646

Given what you have (legacy code with lots of singletons), I think your solution is probably ok. I'd like to share some of my thoughts here.

  1. Yes, introducing SomeSingletonClass class will help with mocking the class, but the downside of that is that it's no longer a singleton. I can easily do val newSingleton = new SomeSingletonClass(), and voila, I have created a new object. The best way to test a singleton is, you know, just test the singleton directly. I guess your singleton will invoke a lot more other singletons, and you don't want to execute all of these side effects. But to properly solve the issue, you'll need to extensively refactor the code.

  2. PowerMock is a very powerful mocking tool in Java (maybe too much power). The way it can mock your static methods/singletons is to modify the byte code. It works in most cases, but when it doesn't, you have absolutely no idea how to fix it. In my previous job at Amazon, we used to PowerMock extensively, but then we slowly stay away from it because of its blackbox magic. One way that helped we achieved that is using dependency injection, and you can inject mock objects in your tests easily.

  3. I can see that you're dealing with legacy code, so it's not easy to rewrite everything (e.g. introduce dependency injection or even the new SomeSingletonClass that you're trying here). My suggestion is that you probably shouldn't spend too much time writing test for legacy code. Instead, when writing new code, try to make it testable, and refactor legacy code as you see fit.

Upvotes: 1

Related Questions