Инъекция фиктивного компонента с аннотацией @MockBean
работает, если ваша зависимость в ClassUnderTest
представлена интерфейсом. Допустим, Dependency
- это простой интерфейс, подобный:
package di.failure.example;
public interface Dependency {
void run();
}
Ваше приложение может предоставить реализацию для этого интерфейса, которая называется DependencyImpl
:
package di.failure.example;
import javax.inject.Singleton;
@Singleton
public class DependencyImpl implements Dependency {
@Override
public void run() {
throw new RuntimeException("I don't want this to load!");
}
}
Теперь для целей теста вы можете определить макет, который заменит DependencyImpl
:
package di.failure.example;
import io.micronaut.test.annotation.MicronautTest;
import io.micronaut.test.annotation.MockBean;
import org.junit.jupiter.api.Test;
import javax.inject.Inject;
import static org.mockito.Mockito.mock;
@MicronautTest
public class ClassUnderTestTest {
@Inject
ClassUnderTest classUnderTest;
@Test
public void test() {
classUnderTest.run();
}
@MockBean(DependencyImpl.class)
public Dependency dependency() {
return mock(Dependency.class);
}
}
Этот тест выполняется, и вместо DependencyImpl
.
используется макет, возвращенный методом
dependency()
.
Использование @Replaces
аннотации
Как и Sergio , упомянутое в разделе комментариев, вы можете заменить зависимость bean-компонента на основе класса, используя аннотацию @Replaces
. Рассмотрим следующий пример:
package di.failure.example;
import io.micronaut.context.annotation.Replaces;
import io.micronaut.test.annotation.MicronautTest;
import org.junit.jupiter.api.Test;
import javax.inject.Inject;
import javax.inject.Singleton;
@MicronautTest
public class ClassUnderTestTest {
@Inject
ClassUnderTest classUnderTest;
@Test
public void test() {
classUnderTest.run();
}
@Replaces(Dependency.class)
@Singleton
public static class MockDependency extends Dependency {
public MockDependency() {
System.out.println("MockDependency.<init>");
}
@Override
void run() {
System.out.println("Does not throw any exception...");
}
}
}
В этом примере мы определили класс MockDependency
и даем указание механизму DI Микронавта заменить боб Dependency
на MockDependency
. Однако есть одна важная вещь, о которой нам нужно помнить - поскольку наш MockDependency
расширяет класс Dependency
, вызывается родительская конструкция. Пример, который вы показали в вопросе, не будет работать в этом случае, потому что Dependency.<init>
выдает RuntimeException
и тест не пройден. В этом модифицированном примере я использовал такой класс:
package di.failure.example;
import javax.inject.Singleton;
@Singleton
public class Dependency {
public Dependency() {
System.out.println("Dependency.<init>");
}
void run() {
throw new RuntimeException("I don't want this to load!");
}
}
Когда я запускаю тест, он проходит, и я вижу следующий вывод консоли:
Dependency.<init>
MockDependency.<init>
Does not throw any exception...
Основное отличие от @MockBean
состоит в том, что в случае @Replaces
вы используете конкретный объект класса. В качестве обходного пути (если нам действительно нужен фиктивный объект Mockito) нужно создать макет внутри и делегировать вызовы этому объекту, что-то вроде этого:
@Replaces(Dependency.class)
@Singleton
public class MockDependency extends Dependency {
private final Dependency delegate;
public MockDependency() {
this.delegate = mock(Dependency.class);
}
@Override
void run() {
delegate.run();
}
}