Unit testing both the presence/absence of a library - java

The question: Is there a way I can use a ClassLoader to test both the presence and absence of the library I'm checking for?
I have a some code which will use a particular library if available, or fall back to some embedded code if not. It works fine but I'm not sure how to unit test both cases in the same run, or if it's even possible. At the moment, my unit tests only check one case because the library is either in the main classpath or it's not.
The code I use to check for the library availability is basically:
boolean available;
try {
Class.forName("net.sf.ehcache.CacheManager");
available = true;
} catch (ClassNotFoundException e) {
available = false;
}
I could potentially alter the way this is done, if it would make unit testing easier.

You are trying to do two things at once:
test the different implementations of your code, and
test that the correct implementation is selected based on the circumstances (or, more generally, that your app works regardless of whether or not the library in question is present).
Separating the two tasks into distinct tests simplifies the problem. Thus, I would implement the two versions of your code as two Strategies, and put the code which checks the classpath and creates the necessary strategy into a Factory (Method). Then both strategies can be unit tested independent of classloader and classpath settings:
interface MyStrategy {
public void doStuff();
}
class MyLibraryUsingStrategy implements MyStrategy {
public void doStuff() {
// call the library
}
}
class MyEmbeddedStrategy implements MyStrategy {
public void doStuff() {
// embedded code
}
}
In unit tests, you can simply create and test either of the concrete strategies:
#Test
void testEmbeddedStrategy() {
MyStrategy strategy = new MyEmbeddedStrategy();
strategy.doStuff();
// assert results
}
A simple factory method to create the appropriate strategy:
MyStrategy createMyStrategy() {
MyStrategy strategy;
try {
Class.forName("net.sf.ehcache.CacheManager");
strategy = new MyLibraryUsingStrategy();
} catch (ClassNotFoundException e) {
strategy = new MyEmbeddedStrategy();
}
return strategy;
}
Since the factory code is fairly trivial, you may even decide not to write automated tests for it. But at any rate, testing the factory is more (part) of an integration test than a unit test; you can simply put together different setups of your app - one with and the other without the library in question - and see that both work properly.

Related

Make SonarQube ignore empty test classes

I have multiple production classes "NaiveHandler", "SmartHandler", "AnotherHandler" that implement the same interface "Handler" and share a lot of code. To reduce code duplication, I extracted an abstract class "AbstractHandler" that contains most of the code, and let the sub-classes inherit and reuse it.
Similarly, the test classes "NaiveHandlerTest", "SmartHandlerTest", "AnotherHandlerTest" for those production classes held tons of duplicate test methods. I moved all these #Test-annotated methods and the test setup out into an abstract class called "HandlerTestBase".
abstract class HandlerTestBase {
protected Handler cut;
protected SomeDependency x;
protected abstract Handler getCutInstance(SomeDependency x);
#BeforeEach
void setup() {
x = Mockito.mock(SomeDependency.class);
cut = getCutInstance(x);
}
#Test
void providesTheRightThing() {
when(x.getSomeValue()).thenReturn("zzz");
var result = cut.doSomething("a");
assertThat(result).isEqualTo("b");
}
// more tests that verify the shared code in AbstractHandler
}
This abstract class cannot (and should not) run on its own. The "~Base" at the end prevents Maven's tools from picking it up and running it. Instead, Maven picks up the individual test classes "NaiveHandlerTest", "SmartHandlerTest", "AnotherHandlerTest", and runs those.
class SmartHandlerTest extends HandlerTestBase {
#Override
protected Handler getCustInstance(SomeDependency x) {
return new SmartHandler(x);
}
// here more tests that only relate to SmartHandler, not the shared code, e.g.
#Test
void specificClassDoesTheRightThing() {
when(x.getSomeValue()).thenReturn("12345");
var result = cut.doSomething("a");
assertThat(result).isEqualTo("y");
}
}
All of this works beautifully.
As one consequence however, one of the test sub-classes, "NaiveHandlerTest" is now empty. It inherits the tests from the abstract class and runs those, but doesn't add any specific tests of its own. For me, this is not a problem.
class NaiveHandlerTest extends HandlerTestBase {
#Override
protected Handler getCustInstance(SomeDependency x) {
return new NaiveHandler(x);
}
}
However, SonarQube picks out this class and reports the code smell "Add some tests to this class", saying that "There's no point in having a JUnit TestCase without any test methods." For other cases, I would agree, but in this pattern SonarQube does not recognize that the test class may look empty but inherits and runs test methods from its super-class.
I could set this finding to "False Positive" to remove it. I could also annotate the seemingly empty test class with #java.lang.SuppressWarnings("java:S2187") to switch off this check here. Is there a still better way to get rid of this finding, such as a tiny tweak to the design that makes SonarQube aware of the existing tests?

cleanup after execution of all tests ( spock framework )

i'm looking for a solution that allow me to handle the setup and the cleanup of my test environment at the launch and the end of my test framework execution.
The setup is not a problem but the cleanup imply to know when the test framework has finished to work or the index of the current test in execution queue.
Has someone a solution to implement this?
You can use org.spockframework.runtime.extension.IGlobalExtension to achieve this, as Spock extensions have callbacks for both BEFORE all specs start, and AFTER all specs end.
public interface IGlobalExtension {
void start();
void visitSpec(SpecInfo spec);
void stop();
}
So, implement stop() in your case to do whatever you need to do.
Spock finds extensions via Java's ServiceLoader, so make sure to add a META-INF/services file (pre-Java9) or declare it in your module-info.java file (post Java9), as explained here: http://spockframework.org/spock/docs/1.1/extensions.html#_writing_custom_extensions
One solution is to create an abstract class that all your specs extend:
class AbstractSpec extends Specification
then inside AbstractSpec find out the classes that are going to be run(for example if you're using spring framework):
private static final synchronized TEST_CLASSES = new ClassPathScanningCandidateComponentProvider(false).with {
addIncludeFilter(new AssignableTypeFilter(AbstractSpec.class))
findCandidateComponents("com/example/test").findAll { it.beanClassName != AbstractSpec.class.canonicalName }
.collect { Class.forName(it.beanClassName) }
}
then inside AbstractSpec do the actual clean up after all classes are run:
def synchronized cleanupSpec() {
TEST_CLASSES.remove(getClass())
if (TEST_CLASSES.isEmpty()) {
// clean up here
}
}
The problem with this solution is that if you run a subset of tests or classes in your IDE; you might have all the test classes in the classpath so TEST_CLASSES won't be emptied so the clean up won't execute.

Mock ClassLoader.getSystemClassLoader().loadClass with Powermockito

I am trying to test utility method which check if particular class is on class path, if yes return true else return false.
Why I am doing this: I have to independent classes extending same class, and only one of it will be on classpath. Need to do specific thing if one particular is on classpath.
Using kind of below method to check if particular class is on class path.
This check will be done only once after first request.
I'd checked Class.forName() also but decided to go with below approach.
My utility method looks something like this:
public static boolean isMyClassOnClassPath() {
try {
ClassLoader.getSystemClassLoader().loadClass("com.MyClass");
return true;
} catch (ClassNotFoundException ex) {
return false;
}
}
Checking false condition is easy as particular class is not not the ClassPath.
I'm trying to write Junit for positive scenario when this method will return true.
#Test
public void isMyClassOnClassPathShouldReturnTrueWhenMyClassIsOnClassPath() throws Exception{
PowerMockito.mockStatic(MyClass.class);
ClassLoader classLoader = PowerMockito.mock(ClassLoader.class);
PowerMockito.mockStatic(ClassLoader.class);
PowerMockito.when(ClassLoader.getSystemClassLoader()).thenReturn(classLoader);
//trying to mock classLoader.loadClass, below way is incorrect
//PowerMockito.when(classLoader.loadClass("com.MyClass")).thenReturn(Class<java.lang.Object.class>);
Assert.assertTrue(MyClassUtil.isMyClassOnClassPath());
}
So is it possible to mock classLoader.loadClass() method?
Honestly: don't even think about doing something like that.
In short, you are like a person sitting on a tree that starts cutting random limbs of the tree that person is sitting on. Meaning: this is a central part of the JVM. Assume your mocking would work: then every caller to that method would receive your mocked loader! So, when your test case itself wanted to load some classes, it would run into your mock!
And as almost usual, when people claim "I need to user Powermock for xyz" your real problem is a different one: you created untestable code. By making that static call there, you prevent yourself from testing your code!
For starters, you can have a look here to learn how to write testable code. But in case you are curious how you could fix your design:
class ClassPathChecker {
private final ClassLoader classLoader;
ClassPathChecker() { this(ClassLoader.getSystemClassLoader()); }
ClassPathChecker(ClassLoader classLoader) {
this.classLoader = this.classLoader);
}
boolean canClassBeLoaded(String className) {
try {
classLoader.loadClass ...
The above uses dependency injection to insert a mocked ClassLoader; which gives you full control over everything that is going on. Without using Powermock at all.
And out of curiosity: why do you restrict yourself to the System classloader? Wouldn't a simple call like Class.forName("yourclass") tell you the same?

How to write unit test by mocking, when you have zero arg:constructors

I was trying to write unit test using jmocks and junit. (My Project uses core java- no frameworks-) I could not write unit test for some of my classes, by mocking external dependencies, when dependencies were initialized in a a no arg-constructor.
As i cannot provide the actual code, trying to explain the scenario by an example
public interface Apple {
String variety();
}
Implementation.
public class MalgovaApple implements Apple {
#Override
public String variety() {
return "Malgova";
}
}
Class to be tested
public class VarietyChecker {
private Apple apple;
VarietyChecker(){
this.apple = new MalgovaApple();
// instead of new, a factory method is used in actual application
}
public String printAppleVariety(){
String variety = apple.variety();
if(variety.length() < 3){
System.out.println("Donot use Code names- Use complete names");
return "bad";
}
return "good";
}
}
Junit test using jmock
public class VarietyCheckerUnitTest{
Mockery context = new JUnit4Mockery();
#Before
public void setUp() throws Exception {
}
#After
public void tearDown() throws Exception {
}
#Test
public void test_VarietyChecker() throws Exception{
final Apple mockapple = context.mock(Apple.class);
VarietyChecker printer = new VarietyChecker();
context.checking(new Expectations(){{
oneOf(mockapple).variety();will(returnValue("as"));
}});
String varietyNameValid = printer.printAppleVariety();
assertEquals("bad",varietyNameValid);
} }
This test fails - Mocking does not work the values "as" is not injected, the test class executes with MalgovaApple ...
Now if we add below constructor to VarietyChecker and use it test case - it gives expected output...
public VarietyChecker(Apple apple) {
super();
this.apple = apple;
}
and in unit test create test class object like
VarietyChecker printer = new VarietyChecker(mockapple);
Exposing a new constructor just for the purpose of testing is not a good idea. After all it is said that you should not alter the code for testing alone, more than that, i am afraid we have already written "some"(amount) code...
Am i missing something in junit or jmock that can make mocking work even incase of no-arg constructors. Or is this a limitation of simple junit and jmocks and should i migrate to something powerful like Jmockit /PowerMock
You should consider two choices.
Use a constructor parameter as you describe.
In this case, you're not "exposing a new constructor just for the purpose of testing". You're making your class more flexible by allowing callers to use a different factory implementation.
Don't mock it.
In this case, you are declaring that it never makes sense to use a different factory. Sometimes this is okay. At that point, the question changes, though. Instead of, "How do I mock this?" your question is now, "What am I gaining from writing this test?" You might not be gaining much of anything, and it might not make much sense to write the test at all.
If you don't mock it and decide a unit test is still worth it, then you should be asserting on other aspects of the code. Either an end state or some output. In this case, the factory call becomes an implementation detail that's not appropriate for mocking.
It's important not to fall for a "unit test everything" mentality. That is a recipe for Test-induced Design Damage. Evaluate your tests on a case by case basis, deciding whether they're providing you any real value or not. Not writing a unit test is a valid option and is even appropriate at times, even if it's option you try very hard to avoid.
Only you can make a determination which one makes the most sense in this case. From the the fact that this is a factory object we're talking about, I'd probably lean toward the former.

Javolution test patterns, dos and don'ts

What are the patterns and dos and don'ts when one is writing tests for Javolution tests? In particular I was wondering:
TestCase.execute() does not allow throwing of exceptions. How to deal with them? Rethrow as RuntimeException or store in a variable and assert in TestCase.validate() or something?
Are there any graphical runners that show you the tests that fail, i.e. in Eclipse? Perhaps someone wrote a JUnit-Wrapper such that I could use the Eclipse JUnit Runner?
The javadoc and javolution sources give some examples and design rationale.
See also an article on serverside.
Javolution tests contain exactly one test, and the exercising of the tested code is separated from the validation into different methods execute() and validate(). Thus the same testclass can be used both for regression tests and speed tests (where the call to validate() is omitted). Also the execution of many tests is trivially parallelizable.
A disadvantages of this separation is: you will get more memory consumption, since the results of the execution of the exercised code needs to be saved until calling validate(). (Freeing those results in tearDown is probably a good idea.)
And if validate comes from a different class than exercise then it might be difficult to debug a failure.
You can get some kind of graphical testrunner by using the following JUnit adapter and running it in eclipse. You can start / debug the failed tests separately. Unfortunately the graphical representation does not include anything about the actual test - it just shows the numbers [0], [1], etc.
#RunWith(Parameterized.class)
public class JavolutionJUnit4Adapter {
protected final javolution.testing.TestCase test;
public JavolutionJUnit4Adapter(javolution.testing.TestCase testcase) {
this.test = testcase;
}
#org.junit.Test
public void executeTest() throws Exception {
enter(REGRESSION);
try {
new javolution.testing.TestSuite() {
#Override
public void run() {
test(test);
}
}.run();
} finally {
exit();
}
}
#Parameters
public static Collection<javolution.testing.TestCase[]> data() {
javolution.testing.TestSuite fp = new WhateverSuiteYouWantToRun();
List<javolution.testing.TestCase> tests = fp.getTestCases();
Collection<javolution.testing.TestCase[]> res = new ArrayList<javolution.testing.TestCase[]>();
for (javolution.testing.TestCase t : tests) {
res.add(new javolution.testing.TestCase[] { t });
}
return res;
}
}

Categories