繁体   English   中英

覆盖 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);
    }

}

我上传了一个最小的 repro 到 Github: https://github.com/crummy/micronaut-test-dependencies 真正的依赖会抛出异常,测试也会抛出异常。 由于我的@MockBean ,我不会预料到会发生这种情况。

如果我将注释更改为@MockBean(Dependency.class)那么我会收到此错误Message: No bean of type [di.failure.example.Dependency] exists 这对我来说似乎更令人困惑——现在它不能解决我真正的或我的模拟依赖?

如果@MockBean的依赖ClassUnderTest由interface表示,则使用@MockBean批注注入mock 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在评论部分中提到的,您可以使用@Replaces注释替换基于类的bean依赖项。 考虑以下示例:

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并指示Micronaut的DI机制用MockDependency替换Dependency bean。 但是,我们需要记住一件重要的事情 - 因为我们的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();
    }
}

我有案例 - controller MyController服务MyService
@MockBean(MyServiceImpl.class)模拟注入的服务。 when(myService.doSomething()).thenReturn...立即调用真正的方法。
我修复了 mocking 问题,给MyService -bean 一个名字并将这个名字传递给@MockBean(name = )

controller:

@Controller
public class MyController {
   private MyService myService;
...
}

工厂:

@Factory
public class MyFactory {

   @Named("myService") // Named just because @MockBean didn't work without it
   @Context
   public MyService myService() {
      return new MyServiceImpl();
   }
}

测试:

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

细节:
micronaut-test-junit5 3.1.1
junit-木星-api 5.6.2

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM