【问题标题】:Injecting mocks with Mockito does not work用 Mockito 注入模拟不起作用
【发布时间】:2017-05-25 20:45:23
【问题描述】:

我正在使用 Mockito 测试我的 Spring 项目,但 @InjectMocks 似乎无法将模拟服务注入另一个 Spring 服务(bean)。

这是我要测试的 Spring 服务:

@Service
public class CreateMailboxService {   
    @Autowired UserInfoService mUserInfoService; // this should be mocked
    @Autowired LogicService mLogicService;  // this should be autowired by Spring

    public void createMailbox() {
        // do mething
        System.out.println("test 2: " + mUserInfoService.getData());
    }

}

下面是我要模拟的服务:

@Service
public class UserInfoService {
    public String getData() {
        return "original text";
    }
}

我的测试代码在这里:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/root-context.xml" })
public class CreateMailboxServiceMockTest {

    @Mock
    UserInfoService mUserInfoService;

    @InjectMocks
    @Autowired
    CreateMailboxService mCreateMailboxService;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void deleteWithPermission() {
        when(mUserInfoService.getData()).thenReturn("mocked text");
    
        System.out.println("test 1: " + mUserInfoService.getData());
    
        mCreateMailboxService.createMailbox();
    }
}

但结果会喜欢

test 1: mocked text
test 2: original text  // I want this be "mocked text", too

似乎 CreateMailboxService 没有得到模拟的 UserInfoService 而是使用 Spring 的自动装配 bean。 为什么我的@InjectMocks 不起作用?

【问题讨论】:

  • 如果你想模拟它们,我认为你需要 MockitoJunitRunner.class。还可以使用 when() 存根来模拟用于 mUserInfoService 的函数。
  • 将 mock 注册为 spring bean。您现在只会从上下文中接收 bean,它不会模拟任何东西(如您所见)。

标签: java spring unit-testing mockito


【解决方案1】:

在测试类中注入时不需要@Autowired 注解。并使用该方法的模拟来获取模拟响应,就像您为 UserInfoService 所做的那样。这将如下所示。 Mockito.when(mCreateMailboxService.getData()).thenReturn("我的回复");

【讨论】:

  • 我认为你不应该嘲笑你正在测试的课程。
【解决方案2】:

您可以在CreateMailboxService 类中为mUserInfoService 创建package 级别设置器。

@Service
public class CreateMailboxService {   
    @Autowired UserInfoService mUserInfoService; // this should be mocked
    @Autowired LogicService mLogicService;  // this should be autowired by Spring

    public void createMailbox() {
        // do mething
        System.out.println("test 2: " + mUserInfoService.getData());
    }

    void setUserInfoService(UserInfoService mUserInfoService) {
        this.mUserInfoService = mUserInfoService;
    }
}

然后,您可以使用 setter 在测试中注入该模拟。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/root-context.xml" })
public class CreateMailboxServiceMockTest {

    @Mock
    UserInfoService mUserInfoService;

    CreateMailboxService mCreateMailboxService;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        mCreateMailboxService = new CreateMailboxService();
        mCreateMailboxService.setUserInfoService(mUserInfoService);
    }

    ...
}

这样可以避免@InjectMocks 和Spring 注解的问题。

【讨论】:

【解决方案3】:

对于那些偶然发现这个线程并使用 JUnit 5 运行的人,您需要替换 @RunWith(SpringJUnit4ClassRunner.class)

@ExtendWith(MockitoExtension.class)
@RunWith(JUnitPlatform.class)

进一步阅读here。不幸的是,使用旧注释使用 JUnit 5 执行测试用例时没有任何提示。

【讨论】:

    【解决方案4】:

    如果您尝试将@Mock 注解用于直接依赖于 Spring 注入的测试,您可能需要将 @Mock 替换为 @MockBean @Inject (两个注释),以及 @InjectMocks@Inject。使用您的示例:

    @MockBean
    @Inject
    UserInfoService mUserInfoService;
    
    @Inject
    CreateMailboxService mCreateMailboxService;
    

    【讨论】:

    • 这为我解决了问题。谢谢!有趣的是,我有其他测试在另一个有效的单元测试类中使用 @Mock 和 @InjectMocks,但是在我进行此切换之前,它在我的这个新测试类中不起作用。
    【解决方案5】:

    对于使用 JUnit 5 运行的用户,您需要将 @RunWith(SpringJUnit4ClassRunner.class) 替换为 @ExtendWith(MockitoExtension.class)

    如需进一步阅读,请查看here

    【讨论】:

      【解决方案6】:

      我的情况非常相似。我把它写下来以防万一任何读者都经历过同样的事情。就我而言,我发现问题在于我在本地类中将注入的变量设置为final

      按照你的例子,我有这样的事情:

      @RunWith(SpringJUnit4ClassRunner.class)
      @ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/root-context.xml" })
      public class CreateMailboxServiceMockTest {
      
          @Mock
          UserInfoService mUserInfoService;
      
          @InjectMocks
          CreateMailboxService mCreateMailboxService = new CreateMailboxService(mUserInfoService);
      
          @Before
          public void setup() {
              MockitoAnnotations.initMocks(this);
          }
      
          @Test
          public void deleteWithPermission() {
              ...
          }
      }
      

      但是在这堂课上我是这样的:

      @Service
      public class CreateMailboxService {   
      
          private final UserInfoService mUserInfoService; // it is NOT injecting Mocks just because it is final! (all ok with private)
      
      
          private final LogicService mLogicService;  // it is NOT injecting Mocks just because it is final! (all ok with private)
      
          @Autowired
          public CreateMailboxService(UserInfoService mUserInfoService, LogicService mLogicService) {
              this.mUserInfoService = mUserInfoService;
              this.mLogicService = mLogicService;
          }
      
          public void createMailbox() {
              ...
          }
      
      }
      

      只需删除final条件,@InjectMocks问题就解决了。

      【讨论】:

        【解决方案7】:

        就我而言,我在使用 JUnit5 时遇到了类似的问题

        @ExtendWith(MockitoExtension.class)
        class MyServiceTest {
        ...
        
        @InjectMocks
        MyService underTest;
        
        @Test
        void myMethodTest() {
        ...
        }
        

        underTest 为空。 问题的原因是我使用了 JUnit4 包中的 @Test import org.junit.Test; 而不是 JUnit5 import org.junit.jupiter.api.Test;

        【讨论】:

          【解决方案8】:

          您可以使用 MockitoJUnitRunner 在单元测试中进行模拟。 在要模拟其行为的类上使用 @Mock 注释。 在您正在测试的类上使用@InjectMocks。 使用新类和初始化类(最好进行依赖注入)或为注入引入 setter 是一种不好的做法。使用 setter 注入只为测试设置依赖项是错误的,因为不应为测试更改生产代码。

          @RunWith(MockitoJUnitRunner.class)
          public class CreateMailboxServiceMockTest {
          
              @Mock
              UserInfoService mUserInfoService;
          
              @InjectMocks
              CreateMailboxService mCreateMailboxService;
          
              @Before
              public void setup() {
                  MockitoAnnotations.initMocks(this);
              }
          
              ...
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2022-12-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多