【问题标题】:Spring + Mockito test injectionSpring + Mockito 测试注入
【发布时间】:2011-10-18 21:07:03
【问题描述】:

我的问题与Injecting Mockito mocks into a Spring bean 中提出的问题非常相似。事实上,我相信那里接受的答案实际上可能对我有用。但是,我的答案有一个问题,然后再做一些进一步的解释,以防答案实际上不是我的答案。

所以我按照上述帖子中的链接访问了 Springockito 网站。我更改了我的 test-config.xml 以包含类似于以下内容:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mockito="http://www.mockito.org/spring/mockito"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.mockito.org/spring/mockito http://www.mockito.org/spring/mockito.xsd">

...

    <mockito:mock id="accountService" class="org.kubek2k.account.DefaultAccountService" />
...
</beans>

目前www.mockito.org 重定向似乎有问题,因此我在https://bitbucket.org/kubek2k/springockito/raw/16143b32095b/src/main/resources/spring/mockito.xsd 找到了XSD 代码,并将xsi:schemaLocation 中的最终条目更改为指向此bitbucket 链接。

运行 mvn test 然后产生以下错误(为便于阅读添加了换行符):

Caused by: org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException:
    Line 43 in XML document from class path resource [spring/test-context.xml] is invalid;
    nested exception is org.xml.sax.SAXParseException; lineNumber: 43; columnNumber: 91;
    The prefix "mockito" for element "mockito:mock" is not bound.

所以关于 Springockito 的问题是:是否有可能再包含这个?我错过了什么?

现在,进行进一步的解释......

我有一个接口,我正在尝试测试其实现:

public interface MobileService {
    public Login login(Login login);
    public User getUser(String accessCode, Date birthDate);
}

该实现包含一个 DAO,Spring @Autowires 为我提供:

@Service
public class MobileServiceImpl implements MobileService {
    private MobileDao mobileDao;

    @Autowired
    public void setMobileDao(MobileDao mobileDao) {
        this.mobileDao = mobileDao;
    }
}

我不想更改我的界面以包含setMobileDao 方法,因为那将添加代码只是为了支持我的单元测试。我试图模拟 DAO,因为这里的实际 SUT 是 ServiceImpl。我怎样才能做到这一点?

【问题讨论】:

    标签: spring mockito


    【解决方案1】:

    您不想测试您的界面:它根本不包含任何代码。你想测试你的实现。所以setter是可用的。就用它吧:

    @Test
    public void testLogin() {
        MobileServiceImpl toTest = new MobileServiceImpl();
        toTest.setMobileDao(mockMobileDao);
        // TODO call the login method and check that it works as expected.
    }
    

    不需要弹簧上下文。只需实例化您的 POJO 服务,手动注入模拟依赖项,然后测试您要测试的方法。

    【讨论】:

    • @Mike 我同意,只需测试实现。 unit 测试没有必要使用 spring 上下文。顺便说一句,Mockito 提供了一些简单的依赖注入机制,结合了@Mock@InjectMocks 注释。你应该看到他们各自的javadoc
    【解决方案2】:

    在解决 Springockito XSD 问题一段时间后,我找到了一个更简单的解决方案。让 Spring 使用工厂方法为您注入模拟,即在 applicationContext.xml 中放入:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
      <bean class="org.mockito.Mockito" factory-method="mock">
        <constructor-arg value="com.gerrydevstory.mycoolbank.AccountsDAO"/>
      </bean>
    
      <bean class="com.gerrydevstory.mycoolbank.BankingService"/>
    
    </beans>
    

    AccountsDAO bean 被注入到 BankingService 类中。对应的JUnit测试用例为:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("/BankingServiceTest.xml")
    public class BankingServiceTest {
    
      @Autowired private BankingService bankingService;
      @Autowired private AccountsDAO mockAccountsDAO;
    
      @Test
      public void testTransfer() throws Exception {
        // Setup 2 accounts
        Account acc1 = new Account();
        acc1.setBalance(800.00);
        Account acc2 = new Account();
        acc2.setBalance(200.00);
    
        // Tell mock DAO to return above accounts when 1011 or 2041 is queried respectively
        when(mockAccountsDAO.findById(1011)).thenReturn(acc1);
        when(mockAccountsDAO.findById(2041)).thenReturn(acc2);
    
        // Invoke the method to test
        bankingService.transfer(1011, 2041, 500.00);
    
        // Verify the money has been transferred
        assertEquals(300.00, acc1.getBalance(), 0.001);
        assertEquals(700.00, acc2.getBalance(), 0.001);
      }
    }
    

    我个人觉得这非常优雅且易于理解。详情请见original blog post

    【讨论】:

      【解决方案3】:

      您有三个选项来设置您的模拟 dao:

      1. 测试实现 - 通过 setDao 方法为您的模拟提供接缝。 (作为 JB 的回答)
      2. 将 setDao 方法添加到界面 - 不需要,因为您不想添加代码只是为了支持您的测试。
      3. 向 impl 类添加构造函数 以接受 dao - 与 #2 的原因相同,不需要。

      如果您想执行 #3,则需要向接受 MobileDao 的 MobileService 添加一个构造函数。

         public MobileServiceImpl(MobileDao mobileDao) {
          this.mobileDao = mobileDao;
      }
      

      那么您的测试将如下所示:

      import static org.mockito.Mockito.verify;
      import static org.mockito.Mockito.*;
      
      import java.util.Date;
      
      import org.junit.Before;
      import org.junit.Test;
      
      public class MobileServiceImplTest {
      
          private MobileService systemUnderTest;
      
          private MobileDao mobileDao;
      
          @Before
          public void setup() {
              mobileDao = mock(MobileDao.class);
              systemUnderTest = new MobileServiceImpl(mobileDao);
          }
      
          @Test
          public void testGetUser() {
              //if you need to, configure mock behavior here. 
              //i.e. when(mobileDao.someMethod(someObject)).thenReturn(someResponse);
              systemUnderTest.getUser("accessCode", new Date());
      
              verify(mobileDao).getUser("JeffAtwood");
          }
      }
      

      请注意,您没有向我们提供 MobileDao 的详细信息,因此我创建了一个接受字符串的 getUser 方法。

      要使测试通过,您的 MobileServiceImpl 只需要这个:

      mobileDao.getUser("JeffAtwood");
      

      【讨论】:

      • 就像在接口中添加 setMobileDao 一样,添加另一个 ctor 来传递 DAO 对象将更改代码以支持单元测试。根据@jb-nizet 的回答,如果我将测试更改为不使用接口而是使用实现,那么我已经可以访问setMobileDao 方法。
      • 了解您不想为了支持测试而在代码中添加任何内容。我过去也做过测试 impl 而不是接口的事情 ;) 只是想提供另一种选择。
      【解决方案4】:

      问题看起来你的类路径不包含实际的 springockito jar - 你不必更改 URL - 这些只是 spring 内部使用的令牌 - 它们没有被解析 - 你只需要一个类路径上足够新的 Spring 发行版和 springockito。

      Kuba(上述库的创建者:))

      【讨论】:

        【解决方案5】:

        我有同样的问题,我想根据他们的 wiki 使用 springockito,但验证 xml 会抛出错误。因此,当我尝试去 xsd 应该在的位置时,却没有。所以我读了这篇文章并开始使用它:

        xmlns:mockito="http://www.mockito.org/spring/mockito"
        xsi:schemaLocation="http://www.mockito.org/spring/mockito 
            https://bitbucket.org/kubek2k/springockito/raw/16143b32095b/src/main/resources/spring/mockito.xsd">
        

        但是当我看这个链接时,感觉很糟糕。在我看来,这不是永久稳定的链接(你必须检查我是否错了)

        【讨论】:

        • 现在链接好像坏了。有人有新的吗?
        猜你喜欢
        • 2021-05-28
        • 1970-01-01
        • 2014-04-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多