【问题标题】:Using @InjectMocks to replace @Autowired field with a mocked implementation使用 @InjectMocks 将 @Autowired 字段替换为模拟实现
【发布时间】:2016-11-25 17:55:43
【问题描述】:

我想用我专门为测试创建的该类的模拟实现替换我的 Spring Boot 应用程序中的自动装配服务类。

我选择创建这个模拟实现是因为这个类的行为过于复杂,无法使用 mockito 进行模拟,因为它本身需要多个其他模拟。

我无法弄清楚如何将这个模拟实现注入到服务中。

这是一个最小的例子:

@Service
public class ServiceIWantToTestImpl implements ServiceIWantToTest{

    @Autowired
    ComplicatedDependency complicatedDependency;

    @Override
    public void methodUsingDependency(){
        String string = complicatedDependency.doSomething();
        System.out.println(string);
    }

}


public class MockComplicatedDependency implements ComplicatedDepencency{

    public MockComplicatedDependency(...){
        // Inject other mocked objects into this mock
    }

    public String doSomthing(){
        // This would be a mocked version of this function for testing
        return "test";
    }

}

@RunWith(MockitoJUnitRunner.class)
public class TestingTheService(){
    @InjectMock
    private static ServiceIWantToTest serviceIWantToTest = new ServiceIWantToTestImpl();

    @Mock
    ComplicatedDependency mockComplicatedDependency;

    @BeforeClass
    public static void init(){
        mockComplicatedDependency = new MockComplicatedDependency(...);
    }

    @Test
    public void testAttempt(){
        serviceIWantToTest.methodUsingDependency();  // This method calls complicatedDependency.doSomething() which does not run the mocked version in MockComplicatedDependency which I wanted to inject, and would always return null instead of the "test" string I put in this example.
    }

}

【问题讨论】:

    标签: java unit-testing junit spring-boot mockito


    【解决方案1】:

    您是否必须使用 Mockito 注释来设置被测类的依赖关系?

    如果这不是主要约束,为什么不做简单的设置并在 ServiceIWantToTestImpl 类中为 ComplicatedDependency 字段引入构造函数或设置器,并在测试中设置依赖项直接设置为您喜欢的任何 ComplicatedDependency impl,例如:

    @Service
    public class ServiceIWantToTestImpl implements ServiceIWantToTest {
    
        @Autowired
        ComplicatedDependency complicatedDependency;
    
        public ServiceIWantToTestImpl() {
    
        }
    
        public ServiceIWantToTestImpl(ComplicatedDependency complicatedDependency) {
            this.complicatedDependency = complicatedDependency;
        }
    
        @Override
        public void methodUsingDependency(){
            String string = complicatedDependency.doSomething();
            System.out.println(string);
        }
    }
    
    public class TestingTheService {
    
        private static ServiceIWantToTestImpl serviceIWantToTest;
    
        @BeforeClass
        public static void init(){
            serviceIWantToTest = new ServiceIWantToTestImpl(new MockComplicatedDependency());
        }
    
        @Test
        public void testAttempt() {
            serviceIWantToTest.methodUsingDependency();
        }
    
    }
    

    这是一种方式。

    要使其与 Mockito 一起使用,您可以像这样使用 @Spy 而不是 @Mock:

    @RunWith(MockitoJUnitRunner.class)
    public class TestingTheService {
    
        @InjectMocks
        private static ServiceIWantToTestImpl serviceIWantToTest = new ServiceIWantToTestImpl();
    
        @Spy
        private static ComplicatedDependency complicatedDependency = new MockComplicatedDependency();
    
        @BeforeClass
        public static void init() {
    
        }
    
        @Test
        public void testAttempt() {
            serviceIWantToTest.methodUsingDependency();
        }
    
    }
    

    虽然这有点小题大做。我强烈建议您阅读 @Spy 注释的 JavaDoc,并确保它的预期用途是您的测试真正需要的。

    【讨论】:

    • 我尝试按照您的建议使用@Spy,效果很好。谢谢!我真的不想引入一个构造函数只是为了测试,我想让spring处理应用程序中的依赖注入。
    • 其实构造函数级注入是推荐的方式,一般不鼓励字段级注入
    • 正如@Milos 提到的,Spring 十年来一直在推荐构造函数注入:spring.io/blog/2007/07/11/…。没有人应该再使用现场注入了。
    • 啊哈,很有趣!感谢您提供的信息,我是一名自学成才的程序员,所以我的知识有很多漏洞。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-01-01
    • 2019-12-04
    • 1970-01-01
    • 1970-01-01
    • 2020-09-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多