Let me show you an example. Assume we have a MyService class: MyService class is a typical implementation of singleton. In addition, it has two methods:
- init(String config)
- to initialize MyService with a config parameter. This method can only call once.
- doSomeService(FileWriter)
- ask MyService to do something. In this example, it writes the value of someConfig via FileWriter parameter.
As mentioned at the beginning, JUnit tests are run in one instance of JVM. If you run both test cases, because of singleton design pattern, the MyService instance of first test case is carried over to the second test case. When the second test case calls the init(...) method, it throws exception. This is actually one of the infamous disadvantages of singleton design pattern. However, you may not be able to re-design your system. Then, how can we fix the unit tests?
Actually, there is a workaround to force reloading of classes for each test using class loader trick. For each JUnit test class, we use a customized test runner which enforce a separated instance of class loader for each test class. Then, the classes we want to test will be reloaded for each JUnit test class Here is the implementation of the test runner (SeparateClassloaderTestRunner):
To use the SeparateClassloaderTestRunner, you need to replace the "@RunWith(MockitoJUnitRunner.class)" to "@RunWith(SeparateClassloaderTestRunner.class)" in the test classes. Then, if you run both test cases in one go, both test cases will pass.
You can get the demo source code at github - https://github.com/loklam/reloadclassdemo.
2 comments:
This is very clever!! A less-clever implementation of a similar idea is durian-globals. If the approach in this blog post had a lot of production-miles, I'd consider switching off of durian-globals towards what you presented here...
This is great work. I experienced the same situation. I used this work to verify the problem from an independent source. Thank you for posting.
Post a Comment