覆盖 Micronaut 测试中的依赖项
Overriding a dependency in a Micronaut test
我正在测试一个注入了 bean 的 Micronaut class。在我的测试中,我提供了 @MockBean
class 来覆盖它。然而,似乎 Micronaut 仍然注入了真正的依赖。
@MicronautTest
public class ClassUnderTestTest {
@Inject ClassUnderTest classUnderTest;
@Test
public void test() {
}
@MockBean
Dependency dependency() {
return mock(Dependency.class);
}
}
我向 Github 上传了一个最小的复制品:https://github.com/crummy/micronaut-test-dependencies。真正的依赖会抛出异常,测试也会抛出异常。由于我的 @MockBean
.
,我没想到会发生这种情况
如果我将注释更改为 @MockBean(Dependency.class)
,则会收到此错误:Message: No bean of type [di.failure.example.Dependency] exists
。这似乎让我更加困惑 - 现在它没有解决我的真实或模拟依赖?
如果您在 ClassUnderTest
中的依赖项由接口表示,则使用 @MockBean
注释注入模拟 bean 是有效的。假设 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);
}
}
执行此测试并使用 dependency()
方法返回的模拟代替 DependencyImpl
。
使用@Replaces
注释
作为 Sergio mentioned in the comments section you can replace class based bean dependency using @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...");
}
}
}
在这个例子中,我们定义了一个 class MockDependency
并且我们指示 Micronaut 的 DI 机制用 MockDependency
替换 Dependency
bean。但是,我们需要记住一件重要的事情——因为我们的 MockDependency
扩展了 Dependency
class,所以调用了父结构。您在问题中显示的示例在这种情况下不起作用,因为 Dependency.<init>
抛出 RuntimeException
并且测试失败。在这个修改过的例子中,我使用了 class 像这样:
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
的情况下,您使用的是具体的 class 对象。作为一种解决方法(如果我们真的需要一个 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();
}
}
我正在测试一个注入了 bean 的 Micronaut class。在我的测试中,我提供了 @MockBean
class 来覆盖它。然而,似乎 Micronaut 仍然注入了真正的依赖。
@MicronautTest
public class ClassUnderTestTest {
@Inject ClassUnderTest classUnderTest;
@Test
public void test() {
}
@MockBean
Dependency dependency() {
return mock(Dependency.class);
}
}
我向 Github 上传了一个最小的复制品:https://github.com/crummy/micronaut-test-dependencies。真正的依赖会抛出异常,测试也会抛出异常。由于我的 @MockBean
.
如果我将注释更改为 @MockBean(Dependency.class)
,则会收到此错误:Message: No bean of type [di.failure.example.Dependency] exists
。这似乎让我更加困惑 - 现在它没有解决我的真实或模拟依赖?
如果您在 ClassUnderTest
中的依赖项由接口表示,则使用 @MockBean
注释注入模拟 bean 是有效的。假设 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);
}
}
执行此测试并使用 dependency()
方法返回的模拟代替 DependencyImpl
。
使用@Replaces
注释
作为 Sergio mentioned in the comments section you can replace class based bean dependency using @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...");
}
}
}
在这个例子中,我们定义了一个 class MockDependency
并且我们指示 Micronaut 的 DI 机制用 MockDependency
替换 Dependency
bean。但是,我们需要记住一件重要的事情——因为我们的 MockDependency
扩展了 Dependency
class,所以调用了父结构。您在问题中显示的示例在这种情况下不起作用,因为 Dependency.<init>
抛出 RuntimeException
并且测试失败。在这个修改过的例子中,我使用了 class 像这样:
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
的情况下,您使用的是具体的 class 对象。作为一种解决方法(如果我们真的需要一个 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();
}
}