1

I am trying to test a class (using Jukito and Mockito), which unfortunately extends another class, which has a static method call. Is it possible to somehow skip this call? I would rather not use PowerMockito.

public class A extends B {

    @Inject
    public A(final String s){
        super(s);
    }
}

public abstract class B {

    private String s;

    protected String m = C.get().createUniqueId(); //Exception is thrown here

    public B(String s){
        this.s = s;
    }
}

public class C {
    private static C c; //assume this is never null

    public static C get() {
        return c;
    }   

    public final native String createUniqueId() {}

}

@RunWith(JukitoRunner.class)
public class ATest {

    @Inject 
    A a;

    @Test
    public void onMethod1Test(){
    }
}

When running ATest, I get the following error:

Error injecting constructor, java.lang.UnsatisfiedLinkError: C

I assumed it's because of a static method, was I wrong?

Note that all the classes are just examples from my real classes and C class was not written by my and can not be changed (unfortunately). But idea behind my classes and these are the same, I just changed names and left only relevant parts.

CrazySabbath
  • 1,274
  • 3
  • 11
  • 33

2 Answers2

1

Jukito claims:

The combined power of JUnit, Guice and Mockito.

But thing is: none of these products allows you to mock static methods.

The only frameworks capable of that: PowerMock(ito) and JMockit.

As you already explained: normally you would "bypass" this "deficiency" by simply writing testable code (that avoids static calls). But as you can't improve your design, you only these two choices: use PowerMock(ito) for testing this class - or not testing it.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • Even though I knew all of this, I guess I was hoping I missed something. Since I'm not too keen on using PowerMockito and can not rewrite C class, at this moment I will not be testing my class. I will accept your answer, since you took your time to write a simple explanation. – CrazySabbath Jul 28 '17 at 09:36
0

So the goal is to use C to generate m on any new instance of B. You don't control C and you're trying to figure out how to test it, right? I think you'll have to pick your poison, but I can think of another choice of "poison" for your situation.

You add a static field on B and give it more access then would otherwise be appropriate:

public abstract class B {
   static C c = C.get();
   private String s;
   protected String m = c.createUniqueId();
   public B(String s){
     this.s = s;
   }
}

Now you can reassign B.c to a mocked instance in your test. I'm more familiar with JUnit and Spock, so I'll try you could figure out the mechanics of that. Since unit tests are in the same package as the class they are testing, you can use the package private scope. If A is in a different package than B, then you'd have to promote it to protected. This is quick and easy, but exposes you to the possibility of other code reassigning B.c. You'd have to judge that risk against the risk of not testing A at all.

You could also consider adding classes and interfaces to hide C from A and B altogether. Essentially, you create something like BFactory that has Supplier<String> to generate m. In unit tests you mock the supplier and in production you use an implementation based on C. Every way I can think of to do this is messy or doesn't force every subclass of B to generate m in the same way. The only exception would be if it actually makes more sense to use composition and put an instance of B on A. Then you might have a decent way to do it.

mymarkers
  • 442
  • 2
  • 4