Question

What I have right now

I have a 3rd party singleton instance that my class under test relies on and that singleton is using System.getenv(String) in its constructor. Is it possible to mock this call?

I tried this

JMockIt Example

    new Expectations()
    {

        System mockedSystem;
        {
            System.getenv( "FISSK_CONFIG_HOME" ); returns( "." );
        }
    };

But it gives me an EXCEPTION_ACCESS_VIOLATION and crashes the JVM.

Is there another way to set a system environment variable for a unit test?

Was it helpful?

Solution

In this case you need to use partial mocking so that JMockit doesn't redefine everything in the System class. The following test will pass:

   @Test
   public void mockSystemGetenvMethod()
   {
      new Expectations()
      {
         @Mocked("getenv") System mockedSystem;

         {
            System.getenv("envVar"); returns(".");
         }
      };

      assertEquals(".", System.getenv("envVar"));
   }

I will soon implement an enhancement so that issues like this don't occur when mocking JRE classes. It should be available in release 0.992 or 0.993.

OTHER TIPS

PowerMock seams to be able to mock system classes.

Your other option (assuming you are not unit testing the 3rd party API) is to create a for Facade the 3rd party API that has a nice, easy mockable interface and have your test classes use this rather than the real thing.

Oh, JMockIt supports this too: package playtest;

import static org.junit.Assert.*;

import mockit.*;
import mockit.integration.junit4.JMockit;

import org.junit.*;
import org.junit.runner.RunWith;

@RunWith(JMockit.class)
public class JMockItTest {

 @Test
 public void mockSystemGetEnv() {
  Mockit.setUpMocks(MockSystem.class); 

  assertEquals("Bye", System.getenv("Hello"));

 }

 @MockClass(realClass = System.class)
 public static class MockSystem {
  @Mock
  public static String getenv(String str) {
   return "Bye";
  }
 }

}

You can't change the environment but you can change the access to it: Simply wrap the call to System.getenv() in a method or a helper class and then mock that.

[EDIT] Now your problem is how to change the code of your third party library. The solution here is to use a Java decompiler and to fix the class. If you want, you can send in a feature request in, too. Add that new class to your test suite. That should make your IDE find the class for the tests.

Since test code doesn't go into production, you can run your tests and the production code will use the original library.

A while back I wanted to test System.exit, and found a solution by using a custom SecurityManager. You can verify the call is being made, and the argument of the call, but using this method, you can't mock the return value of the call.

An update on @Rogério answer.

In my case with JMockit 1.25 I had to do it using the MockUp API:

@Test
public void mockSystemGetenvMethod(){
    new MockUp<System>()
    {
        @Mock
        public String getenv(final String string) {
            return "";
        }
    };

    assertEquals(".", System.getenv("envVar"));
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top