1

I'm trying to test a singleton class with Mockito, something like this:

  public class Single {
     private ID id;
     private Single(){}  //private constructor 
     public static Single getSingle(){ // ***code to get instance*** }

     // method I want to test
     public String getName(){ 
        String name = id.getRawName(); // need field id here
        ** additional operations on name ** // code need to be tested
        return name;
     }

  }

I tried to mock this class and set field "id" with reflection, like this:

Single sg = Mockito.mock(Sincle.class);
Field myID = sg.getClass().getDeclaredField("id"); // fails here
myID.setAccessible(true);
myID.set(sg, <ID instance>);

However it fails at getDeclaredField() method, with exception

java.lang.NoSuchFieldException: id

I guess it's because id field is null in instance sg.

So I'm wondering if there's anything I can do to test this method without modify the original class?

hj690
  • 25
  • 1
  • 5
  • It looks like this answer to this question may help explain some of this behavior. Not entirely sure http://stackoverflow.com/questions/1196192/how-do-i-read-a-private-field-in-java – CollinD Sep 10 '15 at 18:16
  • 2
    Maybe I'm missing something, but why not just `when(sg.getId()).thenReturn("123")`? The point of mocking is to mimic the external API of a unit, which means the internals should generally be irrelevant. – Mark Peters Sep 10 '15 at 18:16
  • If I had to guess why the reflection itself is failing, it would be that `sg.getClass()` returns some dynamic class created by Mockito, which extends `Single`. Using `Single.class.getDeclaredField("id")` would probably work. But you'd still be left with a pretty terrible test IMO. – Mark Peters Sep 10 '15 at 18:20
  • @MarkPeters Thank you for your response. I think your guess makes more sense than mine. And I just modified my example a little to explain why I can't use Mockito.when(). And could you explain a little bit more why this would be a terrible test? – hj690 Sep 10 '15 at 18:27
  • @hj690: From your edit it seems like `ExternalCall` is what you should be mocking. – Mark Peters Sep 10 '15 at 18:31
  • @hj690 Please don't edit important technical details out of your question. I was quoting that portion of your question in my answer, and I'd like to ensure that my answer continues to make sense to help all future readers of this question (in addition to you). – durron597 Sep 10 '15 at 18:43

2 Answers2

1

A mock is not a real object. You can't set a declared field of a mock because it doesn't exist1!

When you are writing tests, you generally want to mock all the classes that are not:

Therefore, the thing you really want to be mocking is ExternalCall, which you don't really explain how it works in your question. However, if it's a static method, you need to use PowerMock. See: Mocking static methods with Mockito

Note that your error is NoSuchFieldException, because you don't actually have a real instance. It's not because:

I guess it's because id field is null in instance sg.

It's because the field actually doesn't exist1 in the mockito generated subclass, not because it's null.

1: It does exist in mock's superclass (in this case, Single), but its value is captured by the mocking behavior and is ignored unless you use it as a partial mock. However, this is a technical detail and not relevant to the proper using of Mockito.

Community
  • 1
  • 1
durron597
  • 31,968
  • 17
  • 99
  • 158
  • @MarkPeters I just edited my example more to explain why I can't just mock ExternalCall. – hj690 Sep 10 '15 at 18:50
  • @hj690 How is `ID` set in your actual app? – durron597 Sep 10 '15 at 18:50
  • @hj690 I don't recommend it, but once you're using a real `Single` object, you can set *that* field with Reflection. But it's better to do it with the way it works in your real app. – durron597 Sep 10 '15 at 19:22
0

...anything I can do to test this method without modify the original class?

Yes, there is. Use Mockito for its bread-and-butter purposes:

when(sg.getId()).thenReturn("123")

Keith
  • 3,079
  • 2
  • 17
  • 26