views:

457

answers:

4

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?

A: 

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.

Aaron Digulla
I think the implication is that he *can't* do that, since it's in a third party library
Brian Agnew
Decompile the class and fix it.
Aaron Digulla
+1  A: 

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.

brianegge
+3  A: 

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";
  }
 }

}
mlk
+2  A: 

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.

Rogerio