3

I want to mock an object with Mockito of a class that has some irrelevant static methods.

There are many questions here on stack overflow that explain that mocking static methods is not possible with Mockito. However, the static methods of the object that I need in my unit test are irrelevant for the test.

More concretely, I want to write a unit test for a method that looks for documents in a cache and in case of a cache miss loads them from a Couchbase lite database. Unfortunately the com.couchbase.lite.Document class has some static methods and trying to mock them

Document mockDocument = Mockito.mock(Document.class);

results in a java.lang.UnsatisfiedLinkError. I intend to mock some of the non-static methods e.g.

doReturn("SomeString").when(mockDocument).getString("someKey");

but the static methods are never used, neither in the tested method nor in the unit test itself. Concerning Couchbase, I guess that the library is not particularly relevant for my question, just the fact that I want to mock an object of some library class which contains both, irrelevant static as well as relevant non-static methods.

Update: Here is the stack trace

java.lang.UnsatisfiedLinkError: com.couchbase.lite.internal.core.C4Log.setLevel(Ljava/lang/String;I)V

at com.couchbase.lite.internal.core.C4Log.setLevel(Native Method)
at com.couchbase.lite.FileLogger.setupDomainObjects(FileLogger.java:84)
at com.couchbase.lite.FileLogger.<init>(FileLogger.java:47)
at com.couchbase.lite.Log.<init>(Log.java:35)
at com.couchbase.lite.AbstractDatabase.<clinit>(AbstractDatabase.java:80)
at com.couchbase.lite.internal.support.Log.sendToLoggers(Log.java:401)
at com.couchbase.lite.internal.support.Log.e(Log.java:247)
at com.couchbase.lite.NativeLibraryLoader.load(NativeLibraryLoader.java:41)
at com.couchbase.lite.Document.<clinit>(Document.java:42)
at sun.reflect.GeneratedSerializationConstructorAccessor10.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:48)
at org.objenesis.ObjenesisBase.newInstance(ObjenesisBase.java:73)
at org.mockito.internal.creation.instance.ObjenesisInstantiator.newInstance(ObjenesisInstantiator.java:19)
at org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker.createMock(SubclassByteBuddyMockMaker.java:47)
at org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker.createMock(ByteBuddyMockMaker.java:25)
at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:35)
at org.mockito.internal.MockitoCore.mock(MockitoCore.java:63)
at org.mockito.Mockito.mock(Mockito.java:1910)
at org.mockito.Mockito.mock(Mockito.java:1819)
at com.my.app.ClassOfTheTest.nameOfTheTest(ClassOfTheTest.java:1234)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:48)
at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:48)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
Aldinjo
  • 394
  • 1
  • 7
  • 21
  • Could you add the complete stacktrace? I suspect the reason why mockito fails to mock the class is mentioned in there. – second Jan 21 '20 at 21:25
  • @second I updated my answer, from my search on stack overflow I assume the problem is that `couchbase.lite.Document`s use static methods that cannot be mocked by Mockito. – Aldinjo Jan 22 '20 at 10:21
  • I tried this with `couchbase-lite-java-core` `1.4.4` but didn't get this error. But I noticed that the `Document` class I tried to mock doesn't have a `getString(...)` method so you might be using a different version. A `UnstatisfiedLinkError` normally indicates that some jar is missing in the class path. – second Jan 22 '20 at 12:44
  • I am using Couchbase lite version 2.6 and I could mock the couchbase.lite.Document class with the old Couchbase lite versions (1.4.x) as well, but the new versions 2.x+ introduced the problem. If my specific problem does not originate in unmockable static methods (opposed to what I asked in my question) I'd be happy to get a solution for that, even though it would be the answer to a different question. – Aldinjo Jan 22 '20 at 13:44
  • The mvn repository doesn't have a jar for the 2.6er version so I wasn't able to verify this. However there is no problem with the [2.7er community version](https://mvnrepository.com/artifact/com.couchbase.lite/couchbase-lite-java). Regarding your question: I have not quite understood what you are trying to do with those static methods, as you mentioned you never use them. For the mocking of a class it should be of no concern whether there are static methods or not. – second Jan 22 '20 at 20:00
  • When searching for the cause of the error, I came to the conclusion that Mockito is not able to Mock the `couchbase.lite.Document` class because of static methods. If this is not the actual cause of me problem, maybe I should ask another more direct question concerning the mocking of Couchbase lite v2.x documents instead. The proposed solution with the use of a mockable wrapper class is working for me though. – Aldinjo Jan 23 '20 at 07:55

1 Answers1

2

Create a DocumentUtils class for these static methods, that way if you wanted to use these methods in the UnitTest you can still do so without mocking it.

DocumentUtils.someMethod(args);

Update:

How the DocumentUtils class may look like (Added some imaginary static methods that likely have no relevance to your use-case) :

public final class DocumentUtils {

public static boolean isDocumentReadable(Document doc) {
    ...
}

public static boolean isDocumentWrittenInEnglish(Document doc) {
    ...
}

public static List<Document> getEnglishWrittenDocuments(List<Document> docs) {
    ...
}

public static boolean areDocumentsTheSame(Document doc1, Document doc2) {
    ...
}

}

Tests:

When it comes to testing you may not even need to reference this class at all...

You will however, need to ensure the mocked Document object that is going to be passed to these methods one way or another, will return the expected value.

For example, if DocumentUtils.isDocumentWrittenInEnglish(Document doc) were to be called at some stage, the mock has to be setup to return the expected value beforehand:

when(mockedDocument.getLanguage()).thenReturn(LANGUAGE_ENGLISH);
JakeB
  • 2,043
  • 3
  • 12
  • 19
  • The utils class will not extend Document, it's purpose is to be able to complete reusable logic and return an answer without requiring any other dependencies other than the objects it is passed. I've updated my answer. – JakeB Jan 21 '20 at 12:28
  • Thanks for the clarification! So basically you suggest to move the static methods to a separate class (i.e. DocumentUtils) in order to mock the remaining non-static object regularly, right? My problem is now that the "Document" object that I want to mock is from a library (namely Couchbase lite), and the static methods are implemented inside this library. Thus, it is not so easy to split the class as it would be if I wrote the class myself. – Aldinjo Jan 21 '20 at 13:33
  • 1
    Then i would suggest creating a DocumentWrapper class to get around this. https://stackoverflow.com/questions/889160/what-is-a-wrapper-class – JakeB Jan 21 '20 at 13:42
  • With the implication that the wrapper class would then have to be used in production code instead of the original Document class? So using a wrapper instead of direct access would be the sacrifice to improve testability, right? – Aldinjo Jan 21 '20 at 13:56
  • Correct. Wrapping is one solution, another may be to use PowerMock, however, i haven't used this before so cant provide more info, seems like a lot of work for something that should be solvable with correct architecture, however in your scenario might be worth a look... https://github.com/powermock/powermock/wiki/Mockito#mocking-static-method – JakeB Jan 21 '20 at 14:01