javamultithreadingmockitoguice

Mockito mock return value of static method of util class in a different thread


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


Solution

  • 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.