I have a method that calls a Util class' static method.
Is there a way to mock the return value of this static method so that it always returns a static value?
I want to do this because this static method returns me some configurations that could change, but I don't want this to affect my test. If I could mock this Util class and return a static results for the method all the time, it would give me a stable test.
public class SomeService{
public void doSth(){
SomeConfig config = UtilClass.getConfigForId(“id”);
...
}
}
I tried the mockStatic
method mentioned here but it didn't work:
// get SomeService instance from Guice container
Injector injector = Guice.createInjector(new BasicModule());
SomeService someService = injector.getInstance(SomeService.class);
try (MockedStatic<UtilClass> dummy = Mockito.mockStatic(UtilClass.class)) {
dummy.when(() -> UtilClass.getConfigForId(any()))
.thenReturn(someConfig);
// Didn't give me someConfig above, but the actual value
someService.doSth();
}
UPDATE:
Apologies for all the typos made. I've now corrected them so that the code is syntax-error free.
I think I found what might have broke the MockedStatic: that UtilClass.getConfigForId
is called in another thread. Here's the overall structure:
public class SomeService{
public void doSth(List<Msg> messages){
List<Callable<Void>> tasks = new ArrayList<>();
for (Msg msg : messages){
tasks.add(()->{
String id = msg.getId();
SomeConfig config = UtilClass.getConfigForId(id);
...
})
}
// use Executors.newWorkStealingPool() to execute the tasks
...
}
}
As long as UtilClass.getConfigForId(id)
is not executed in the same thread as the try with resource
code, the magic doesn't work. I've tried another function that does the same but in one thread and MockedStatic
works there fine.
So the question becomes: how to make MockedStatic
work in a different thread?
Another UPDATE:
I think from the javaDoc it's not possbile. So I'll have to come up with other ways, e.g. refactoring
CodePudding user response:
Your tested code doesn't seem to match your test code. In SomeService#doSth
you're calling UtilClass.getConfigForId()
, yet in the test you're mocking a method with different signature: UtilClass.getConfigForId("id")
.
If you use a method with String parameter in your code, the stubbing has to match it, so if in the test you expect a value "id"
, it should be passed to the method in the tested code (SomeService
) as well (values are compared using equals
method in Mockito by default). Otherwise, you should use an ArgumentMatcher like any().
var id = "id";
var mockedValue = "test value";
var someConfig = new SomeConfig(mockedValue);
var someService = new SomeService();
try (MockedStatic<UtilClass> dummy = Mockito.mockStatic(UtilClass.class)) {
dummy.when(() -> UtilClass.getConfigForId(id))
.thenReturn(someConfig);
var result = someService.doSth(id);
assertEquals(mockedValue, result);
}
var mockedValue = "test value 2";
var someConfig = new SomeConfig(mockedValue);
var someService = new SomeService();
try (MockedStatic<UtilClass> dummy = Mockito.mockStatic(UtilClass.class)) {
dummy.when(() -> UtilClass.getConfigForId(any()))
.thenReturn(someConfig);
var result = someService.doSth("some id");
assertEquals(mockedValue, result);
}
If the signature of the method you're using in the tested code does not include a parameter, it should be mocked without a parameter as well (in case of for example an overloaded method).
I've tested the code above and all tests pass - you can verify it in a GitHub repository.