简体   繁体   中英

How to mock setting static class property in legacy Java code for JUnit 4 unit testing

I am writing unit tests using JUnit 4.13 and Mockito core 4.9.0. Following is a simplified version of the class being tested which is part of a legacy code that I don't want to refactor:

//JAR com.ibm.mq-8.0.jar
import com.ibm.mq.MQEnvironment;
public class LegacyCodeToTest
  public void doSomething() {
    ...some other code
    MQEnvironment.sslCipherSuite = "some-cipher-value";
    ...some other code
  }
}

Following is the unit test:

@RunWith(MockitoJUnitRunner.class)
public class TestLegacyCodeToTest {
  @Test
  public void testDoSomething() {
    LegacyCodeToTest someVar = new LegacyCodeToTest();
    mock(MQEnvironment.class);
    when(MQEnvironment.sslCipherSuite).thenReturn(null);
    someVar.doSomthing();
  }
}

The problem I am facing is that the unit I am working with needs many other dependencies I cannot provide during unit testing and the line of code MQEnvironment.sslCipherSuite = "some-cipher-value" or even just referencing MQEnvironment.sslCipherSuite in the test is causing the run to crash due to missing dependencies which I cannot troubleshoot. I want to mock this line of code so that it has no effect as if it doesn't exist and I don't care about it during unit testing. Now I had to comment out this line to let it work.

Also, I know there is something wrong with the way I implemented mocking but I could not even reach that point since the test is crashing due to missing dependencies.

So, the bottom line is how I can mock such a line of code:

MQEnvironment.sslCipherSuite = "some-cipher-value";

Mind you, I don't want to refactor the class as this will raise the need to trigger writing a lot of other unit testing and it won't be allowed.

Having looked at the linked document I don't think it is possible to test doSomething() as it stands.
MQEnvironment represents the global environment for MQ, given that doSomething sets the values before it presumable then makes calls to MQQueueManager you are stuck with what is defined in doSomething .

In general when I find something is hard to test I feel I have made suboptimal design decisions.

Given this is a legacy system, I would introducing an interface to represent LegacyCodeToTest that way you give yourself a hook into the legacy code that will allow you to replace LegacyCodeToTest when you are ready.

If you are very lucky and LegacyCodeToTest is being passed into classes or methods you may get some benefits from using an interface such as simpler mocking as well.

If you are going to be doing a lot of work with legacy systems I am told (though haven't read it myself) that "Working Effectively with Legacy Code" by Michael C. Feathers is a good book to have by your side.

Anyway, a code example:

public interface CodeToTest {
   void doSomething();
   ... other method declarations as needed.
   ... if you are using Java 8+ new features of interfaces such as default 
   ... methods might be useful, but remember "with great power comes great responsibility" etc etc 
}

public LegacyCodeToTest implements CodeToTest {
    ... no other changes needed
}

public SomeClassThatUsesTheLegacyClass {

   // public aMethod(LegacyCodeToTest lctt) {
   public aMethod(CodeToTest lctt) {
      lctt.doSomething();
   }
}

This is a (small?) step in the right direction.
In the future you can introduce a better designed class to replace LegacyCodeToTest perhaps something like this:

public NewCodeToTest implements CodeToTest {
    private final String cypher;
    ... other MQ Environmental properties as needed.

    public NewCodeToTest(String cypher) {
       this.cypher = cypher
    }

    public void doSomething() {
       ...some other code
       MQEnvironment.sslCipherSuite = cypher;
       ...some other code
    }
}

This class is easier to work with as its collaborators/configuration are "injected" into the class at construction.
It still makes use of the MQEnvironment which I dont much like, so I would look to somehow abstract that or have a single API that deals with MQ, perhaps building it around or making use of JMS or AMQP.

This would be more or less my approach with the information I have to hand.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM