Mockito: добавление реальных объектов в частные поля @Autowired


191

Я использую Mockito @Mockи @InjectMocksаннотации для вставки зависимостей в частные поля, которые отмечены Spring @Autowired:

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {
    @Mock
    private SomeService service;

    @InjectMocks
    private Demo demo;

    /* ... */
}

и

public class Demo {

    @Autowired
    private SomeService service;

    /* ... */
}

Теперь я хотел бы также вводить реальные объекты в частные @Autowiredполя (без установщиков). Это возможно, или механизм ограничен только инъекциями?


5
Обычно, когда вы издеваетесь над чем-то, это означает, что вы не особо заботитесь о конкретном объекте; что вы действительно заботитесь только о поведении высмеиваемого объекта. Возможно, вы хотите вместо этого сделать интеграционный тест? Или не могли бы вы обосновать, почему вы хотите, чтобы смешанные и конкретные объекты жили вместе?
Макото

2
Ну, я имею дело с унаследованным кодом, и потребовалось бы много операторов when (...). ThenReturn (...) для настройки макета, чтобы предотвратить некоторые NPE и тому подобное. С другой стороны, для этого можно безопасно использовать реальный объект. Поэтому было бы очень удобно иметь возможность вводить реальные объекты вместе с макетами. Даже если это может быть запахом кода, я считаю разумным в данном конкретном случае.
user2286693

Не забудьте MockitoAnnotations.initMocks(this);в @Beforeметоде. Я знаю, что это не имеет прямого отношения к первоначальному вопросу, но для любого, кто придет позже, это нужно добавить, чтобы сделать его работоспособным.
Cuga

7
@Cuga: если вы используете бегун Mockito для JUnit, ( @RunWith(MockitoJUnitRunner.class)), вам не нужна строкаMockitoAnnotations.initMocks(this);
Клинт Иствуд

1
Спасибо ... Я никогда не знал этого и всегда определял оба
Cuga

Ответы:


306

Используйте @Spyаннотацию

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {
    @Spy
    private SomeService service = new RealServiceImpl();

    @InjectMocks
    private Demo demo;

    /* ... */
}

Mockito будет рассматривать все поля, имеющие @Mockили @Spyаннотацию в качестве потенциальных кандидатов, которые будут введены в экземпляр, помеченный @InjectMocksаннотацией. В приведенном выше случае 'RealServiceImpl'экземпляр будет введен в «демо»

Для более подробной информации обратитесь

Mockito-дом

@Spy

@Mock


9
+1: работал для меня ... за исключением объектов String. Мокито жалуется:Mockito cannot mock/spy following: - final classes - anonymous classes - primitive types
Адриан Пронк

Спасибо. Это сработало и для меня :). Чтобы заставить прокси использовать
псевдоним

Спасибо! Это именно то, что мне было нужно!
nterry

2
В моем случае, Мокито не вводит Шпиона. Это делает инъекцию, хотя. Поле приватное и без сеттера.
Vituel

8
Кстати, нет никакой необходимости new RealServiceImpl(), @Spy private SomeService service;создает реальный объект , используя конструктор по умолчанию , прежде чем шпионить это в любом случае.
parxier

20

В дополнение к бланковому ответу @Dev, если вы хотите использовать существующий компонент, созданный Spring, код можно изменить на:

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {

    @Inject
    private ApplicationContext ctx;

    @Spy
    private SomeService service;

    @InjectMocks
    private Demo demo;

    @Before
    public void setUp(){
        service = ctx.getBean(SomeService.class);
    }

    /* ... */
}

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


2
@ Аада Можете ли вы уточнить?
Йоаз Менда

1
Из какой библиотеки это происходит, я могу видеть только InjectMocks в org.mockito
Sameer

1
@sameer import org.mockito.InjectMocks;
Йоаз Менда

Добавление комментария, чтобы отменить Аада. Это сработало для меня. Я не мог «просто не использовать инъекцию полей», как предлагали другие ответы, поэтому я должен был убедиться, что Autowiredполя из зависимостей были созданы правильно.
Скрамбо

1
Я попробовал это. но я
получаю

3

Mockito - это не DI-фреймворк, и даже DI-фреймворки поощряют инжекторы конструктора поверх полевых.
Таким образом, вы просто объявляете конструктор для установки зависимостей тестируемого класса:

@Mock
private SomeService serviceMock;

private Demo demo;

/* ... */
@BeforeEach
public void beforeEach(){
   demo = new Demo(serviceMock);
}

Использование Mockito spy для общего случая - ужасный совет. Это делает тестовый класс ломким, а не прямым и подверженным ошибкам: что на самом деле насмехается? Что действительно проверено?
@InjectMocksа @Spyтакже вредит общему дизайну, так как он поощряет раздутые классы и смешанные обязанности в классах.
Пожалуйста, прочитайте spy()Javadoc, прежде чем использовать это вслепую (акцент не мой):

Создает шпион реального объекта. Шпион вызывает реальные методы, если они не заглушены. Настоящих шпионов следует использовать осторожно и время от времени , например, когда имеешь дело с устаревшим кодом.

Как обычно, вы собираетесь прочитать partial mock warning : Объектно-ориентированное программирование решает проблему сложности, разделяя ее на отдельные конкретные объекты SRPy. Как частичная имитация вписывается в эту парадигму? Ну, просто нет ... Частичная имитация обычно означает, что сложность была перенесена в другой метод для того же объекта. В большинстве случаев это не тот способ, которым вы хотите разработать свое приложение.

Тем не менее, есть редкие случаи, когда частичные имитации полезны: работая с кодом, который вы не можете легко изменить (сторонние интерфейсы, временный рефакторинг унаследованного кода и т. Д.) Однако я бы не использовал частичные имитации для новых, управляемых тестами разработан код.


0

Весной ReflectionTestUtilsдля этой цели существует специальная утилита . Возьмите конкретный экземпляр и введите в поле.


@Spy
..
@Mock
..

@InjectMock
Foo foo;

@BeforeEach
void _before(){
   ReflectionTestUtils.setField(foo,"bar", new BarImpl());// `bar` is private field
}

-1

Я знаю, что это старый вопрос, но мы столкнулись с той же проблемой при попытке ввести строки. Поэтому мы изобрели расширение JUnit5 / Mockito, которое делает именно то, что вы хотите: https://github.com/exabrial/mockito-object-injection

РЕДАКТИРОВАТЬ:

@InjectionMap
 private Map<String, Object> injectionMap = new HashMap<>();

 @BeforeEach
 public void beforeEach() throws Exception {
  injectionMap.put("securityEnabled", Boolean.TRUE);
 }

 @AfterEach
 public void afterEach() throws Exception {
  injectionMap.clear();
 }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.