[英]Overriding a dependency in a Micronaut test
I'm testing a Micronaut class that has a bean injected into it.我正在测试一个注入了 bean 的 Micronaut class。 In my test I provide a
@MockBean
class to override it.在我的测试中,我提供了一个
@MockBean
class 来覆盖它。 However, it seems Micronaut still injects the real dependency.然而,似乎 Micronaut 仍然注入了真正的依赖。
@MicronautTest
public class ClassUnderTestTest {
@Inject ClassUnderTest classUnderTest;
@Test
public void test() {
}
@MockBean
Dependency dependency() {
return mock(Dependency.class);
}
}
I uploaded a minimum repro to Github: https://github.com/crummy/micronaut-test-dependencies .我上传了一个最小的 repro 到 Github: https://github.com/crummy/micronaut-test-dependencies 。 The real dependency throws an exception, and the test does too.
真正的依赖会抛出异常,测试也会抛出异常。 I would not have expected this to happen because of my
@MockBean
.由于我的
@MockBean
,我不会预料到会发生这种情况。
If I change the annotation to be @MockBean(Dependency.class)
then I get this error: Message: No bean of type [di.failure.example.Dependency] exists
.如果我将注释更改为
@MockBean(Dependency.class)
那么我会收到此错误Message: No bean of type [di.failure.example.Dependency] exists
。 This seems even more confusing to me - now it doesn't resolve my real or my mock dependency?这对我来说似乎更令人困惑——现在它不能解决我真正的或我的模拟依赖?
Injecting mock bean with @MockBean
annotation works if your dependency in ClassUnderTest
is represented by interface. 如果
@MockBean
的依赖ClassUnderTest
由interface表示,则使用@MockBean
批注注入mock bean。 Let's say Dependency
is a simple interface like: 让我们说
Dependency
是一个简单的界面,如:
package di.failure.example;
public interface Dependency {
void run();
}
Your application may provide an implementation for this interface called DependencyImpl
: 您的应用程序可能会为此接口提供名为
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!");
}
}
Now, for test purpose you can define a mock that replaces DependencyImpl
: 现在,出于测试目的,您可以定义一个替换
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);
}
}
This test executes and the mock returned by dependency()
method is used in place of DependencyImpl
. 执行此测试并使用
dependency()
方法返回的模拟代替DependencyImpl
。
@Replaces
annotation @Replaces
注释 As Sergio mentioned in the comments section you can replace class based bean dependency using @Replaces
annotation. 正如Sergio在评论部分中提到的,您可以使用
@Replaces
注释替换基于类的bean依赖项。 Consider following example: 考虑以下示例:
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...");
}
}
}
In this example we have defined a class MockDependency
and we instruct Micronaut's DI mechanism to replace Dependency
bean with MockDependency
. 在这个例子中,我们定义了一个类
MockDependency
并指示Micronaut的DI机制用MockDependency
替换Dependency
bean。 However, there is one important thing we need to remember about - because our MockDependency
extends Dependency
class, parent construct gets invoked. 但是,我们需要记住一件重要的事情 - 因为我们的
MockDependency
扩展了Dependency
类,所以调用了父构造。 The example you have shown in the question won't work in this case, because Dependency.<init>
throws RuntimeException
and the test fails. 在这种情况下,您在问题中显示的示例将不起作用,因为
Dependency.<init>
会抛出RuntimeException
并且测试失败。 In this modified example I have used class like this one: 在这个修改过的例子中,我使用了类似这样的类:
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!");
}
}
When I run the test it passes and I see following console output: 当我运行测试时,它会通过,我会看到以下控制台输出:
Dependency.<init>
MockDependency.<init>
Does not throw any exception...
The main difference comparing to @MockBean
is that in case of @Replaces
you are using a concrete class object. 与
@MockBean
相比的主要区别在于,在@Replaces
情况下,您使用的是具体的类对象。 As a workaround (if we really need a Mockito mock object) is to create a mock internally and delegate calls to this object, something like this: 作为一种解决方法(如果我们真的需要一个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();
}
}
I had the case - controller MyController
with service MyService
.我有案例 - controller
MyController
服务MyService
。
@MockBean(MyServiceImpl.class)
did't mock injected service. @MockBean(MyServiceImpl.class)
模拟注入的服务。 when(myService.doSomething()).thenReturn...
immediately called real method. when(myService.doSomething()).thenReturn...
立即调用真正的方法。
I fixed mocking issue with giving MyService
-bean a name and passing this name to @MockBean(name = )
我修复了 mocking 问题,给
MyService
-bean 一个名字并将这个名字传递给@MockBean(name = )
controller: controller:
@Controller
public class MyController {
private MyService myService;
...
}
factory:工厂:
@Factory
public class MyFactory {
@Named("myService") // Named just because @MockBean didn't work without it
@Context
public MyService myService() {
return new MyServiceImpl();
}
}
test:测试:
@MicronautTest
class MyControllerTest {
@Inject
MyService myService;
@Inject
@Client("/")
@HttpClient client;
@MockBean(named = "myService")
MyService mockMyService() {
return mock(MyService.class);
}
@Test
void test() {
when(myService.doSomething()).thenReturn(genDto());
...
}
}
Details:细节:
micronaut-test-junit5 3.1.1
micronaut-test-junit5
3.1.1
junit-jupiter-api 5.6.2
junit-木星-api
5.6.2
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.