Mockito имитирует возвращаемое значение статического метода класса util в другом потоке

У меня есть метод, который вызывает статический метод класса Util. Есть ли способ смоделировать возвращаемое значение этого статического метода, чтобы он всегда возвращал статическое значение?

Я хочу сделать это, потому что этот статический метод возвращает мне некоторые конфигурации, которые могут измениться, но я не хочу, чтобы это повлияло на мой тест. Если бы я мог издеваться над этим классом Util и постоянно возвращать статические результаты для метода, это дало бы мне стабильный тест.

public class SomeService{
  public void doSth(){
    SomeConfig config = UtilClass.getConfigForId(“id”);
    ...
  }
}

Я попробовал mockStatic метод, упомянутый здесь, но он не сработал:

// 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();
}

ОБНОВЛЯТЬ:

Приносим свои извинения за все допущенные опечатки. Теперь я исправил их, чтобы в коде не было синтаксических ошибок. Я думаю, что нашел то, что могло сломать MockedStatic: этот UtilClass.getConfigForId вызывается в другом потоке. Вот общая структура:

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

Пока UtilClass.getConfigForId(id) не выполняется в том же потоке, что и код try with resource, магия не работает. Я пробовал другую функцию, которая делает то же самое, но в одном потоке, и MockedStatic там работает нормально. Таким образом, возникает вопрос: как заставить MockedStatic работать в другом потоке? Еще одно ОБНОВЛЕНИЕ: Я думаю, что из javaDoc это невозможно. Поэтому мне придется придумать другие способы, например. рефакторинг

В опубликованном вами коде вы издеваетесь над классом обслуживания, а не над классом утилиты Mockito.mockStatic(SomeService.class)

XtremeBaumer 11.01.2023 12:05

@XtremeBaumer прав - тестовый код даже не компилируется (несовместимые типы).

Jonasz 11.01.2023 19:06

Извините за опечатку @XtremeBaumer. Я исправил их. И я, кажется, выяснил, почему у меня не работает MockedStatic: что он вызывается другим потоком.

waynewingsyd 12.01.2023 00:35
Лучшая компания по разработке спортивных приложений
Лучшая компания по разработке спортивных приложений
Ищете лучшую компанию по разработке спортивных приложений? Этот список, несомненно, облегчит вашу работу!
Blibli Automation Journey - Как захватить сетевой трафик с помощью утилиты HAR в Selenium 4
Blibli Automation Journey - Как захватить сетевой трафик с помощью утилиты HAR в Selenium 4
Если вы являетесь веб-разработчиком или тестировщиком, вы можете быть знакомы с Selenium, популярным инструментом для автоматизации работы...
Фото ️🔁 Radek Jedynak 🔃 on ️🔁 Unsplash 🔃
Фото ️🔁 Radek Jedynak 🔃 on ️🔁 Unsplash 🔃
Что такое Java 8 Streams API? Java 8 Stream API
Деревья поиска (Алгоритм4 Заметки к учебнику)
Деревья поиска (Алгоритм4 Заметки к учебнику)
(1) Двоичные деревья поиска: среднее lgN, наихудшее N для вставки и поиска.
2
3
81
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваш протестированный код не соответствует вашему тестовому коду. В SomeService#doSth вы вызываете UtilClass.getConfigForId(), но в тесте вы издеваетесь над методом с другой сигнатурой: UtilClass.getConfigForId("id").

Если вы используете в коде метод со строковым параметром, заглушка должна ему соответствовать, поэтому, если в тесте вы ожидаете значение "id", оно должно быть передано и методу в тестируемом коде (SomeService) (значения сравниваются используя метод equals в Mockito по умолчанию). В противном случае вы должны использовать ArgumentMatcher, как и 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);
}

Если сигнатура метода, который вы используете в тестируемом коде, не включает параметр, он также должен быть имитирован без параметра (например, в случае перегруженного метода).


Я протестировал приведенный выше код, и все тесты проходят — вы можете проверить это в репозитории GitHub.

Спасибо за отличный ответ и код @Jonasz. Извиняюсь за опечатку в моем вопросе - я дважды проверил и могу убедиться, что подписи и значения параметров совпадают в моем коде. Так что это все еще не работает для меня. Будет ли это иметь какое-то отношение к тому, что мой someService в тесте управляется Guice (что-то вроде Spring)?

waynewingsyd 11.01.2023 06:32

Не могли бы вы сделать так, чтобы код в вашем вопросе максимально отражал ваш фактический код (включая весь метод тестирования)? К сожалению, трудно предположить, но я не думаю, что инструмент внедрения зависимостей, такой как Guice, вызовет проблемы с mockStatic, поэтому я полагаю, что это может быть что-то другое.

Jonasz 11.01.2023 07:31

Йонаш Я обновил вопрос. Вы правы, инструменту инъекций нечего делать, но многопоточность делает. Вы не против проверить мое обновление?

waynewingsyd 12.01.2023 00:31

Однако вы правы в своем последнем обновлении - имитирующие статические методы следует использовать только тогда, когда у вас нет контроля над тестируемым кодом, в противном случае лучше избегать таких статических методов, поскольку они значительно усложняют тестирование кода. Было бы лучше использовать DI (поскольку вы уже используете Guice), чтобы внедрить класс, создающий/загружающий конфигурацию, и заменить его в тесте макетом, над которым у вас будет полный контроль. Класс поставщика конфигурации также должен иметь свои собственные тесты.

Jonasz 12.01.2023 06:51

Другие вопросы по теме