【问题标题】:unit testing mock injection单元测试模拟注入
【发布时间】:2013-03-13 22:12:42
【问题描述】:

我正在尝试对我的课程进行单元测试,并模拟 DAO 以提供可预测的结果。尽管如此,当我收到休眠错误时,似乎仍在调用我的 DAO 方法。

org.hibernate.exception.GenericJDBCException: Station TST not found.

这个错误是因为我的数据库不包含 TST,但是因为我模拟了 DAO,所以不应该调用它吗?如何模拟调用以使数据库不被命中。

这是我设置模拟的方式

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
public class MyServiceTest{

    @Autowired
    private MyService service;

    private MyDAO dao;

    private LinkedList objs;    


    @Before
    public void init() throws SecurityException, NoSuchFieldException,
            IllegalArgumentException, IllegalAccessException {


        // mock the dao for predictable results
        dao = mock(MyDAO.class);

        when(dao.myDBCall(anyString(), any(Date.class))).thenReturn(legs);
        Field f = MyService.class.getDeclaredField("dao");
        f.setAccessible(true);
        f.set(service, dao);


    }

    @Test
    public void testCall() {
        // construct our sample leg data
        legs = new LinkedList();
//fill in my stub data i want to return

        Collections.shuffle(legs);

        List results = service.testingCall("TST", new Date()); // this fails because service is using the dao, but it is making a call to the DB with this garbage 'Test' param rather than just returning 'legs' as stated in my when clause
        assertThat(results, not(nullValue()));


    }

    @Test
    public void testGetGates() {
        // fail("Not yet implemented");
    }

    @Test
    public void testGetStations() {
        // fail("Not yet implemented");
    }
}

【问题讨论】:

    标签: java spring hibernate junit mockito


    【解决方案1】:

    我认为你的 Service 是由 spring 实例化的,你的 Dao 在你的 application-context.xml 中定义,你甚至在尝试将你的模拟注入你的服务之前就得到了错误。

    我喜欢用 Autowired 构造函数来测试我的服务,然后在我的测试中我不通过 spring 实例化我的服务,而是使用构造函数和 mock。

    private MyDao myDao;
    
    @Autowired
    public MyService(MyDao myDao){
    this.myDao = myDao;
    }
    

    然后在你的测试中:

    MyService myService = new Myservice(mockedDao);
    

    【讨论】:

    • 这个错误肯定是在我注入我的模拟之后出现的。我可以通过调试器来判断
    • 您的服务是否包含其他 dao ?
    • 不只是一个,但它确实包含我需要自动注入的其他 bean
    • 75inchpianist,请检查我的答案... @ContextConfiguration(locations = { "classpath:applicationContext.xml" }) 将导致完整的 Spring 上下文和休眠开始(并且模拟无法阻止它)
    • 这些 bean 需要更多的 dao 吗?此外,@MichailNikolaev 说的是对的,如果您加载所有 springcontext,您很可能会访问数据库。您可以做的是手动实例化您的 bean(如我们建议的那样)或创建另一个上下文。如果因为依赖的数量太痛苦,那么你的类服务存在隔离问题,你可能想要拆分它。
    【解决方案2】:

    首先,检查PowerMock,而不是Field f = MyService.class.getDeclaredField("dao");(检查Whitebox

    模拟一个对象不会阻止休眠启动(因为您正在加载applicationContext.xml,所以休眠将尝试连接到数据库)。您的模拟 - 它只是一个测试实例的一个字段中的 POJO,而不是 Spring bean 或类的替代品。

    如果您只想测试 dao 和服务类(创建清晰的单元测试)- 从测试中删除与 Spring 相关的注释并自己进行(创建模拟、创建服务、将模拟放入服务并对其进行测试)。

    如果您想使用 Spring 上下文测试您的应用程序(创建集成测试)- 创建 H2 in-memory database 用于休眠并只测试您的服务(不要忘记在 @After 中清除它)。

    第三种方法 - 将您的配置文件拆分为两个 Spring 配置文件(例如 devtest)并自己实现 mock(将 mock 放到 test,real hibernate 和 dao 放到 dev)。

    如果您不想使用 Spring 配置文件 - 可以将 applicationContext.xml 拆分为 3 个文件(用于普通 bean、用于真正的 DB bean、用于模拟)。

    还有一种更性感的方式——使用springockito-annotations(但你仍然需要避免加载休眠)

    【讨论】:

    • 不幸的是,H2 不是一个选项。至于“自己做”,我的服务还有许多其他需要自动注入的 bean,这使得这种方法不实用。真的没有办法完全嘲讽道吗?
    • 你可以。首先,将与休眠相关的 xml 配置移动到一些(“开发”)Spring 配置文件(谷歌为“spring 配置文件”)。然后创建 dao 的存根(模拟)实现并将其放入“测试”配置文件。在测试中添加 @ActiveProfiles("test") 注释后(您还需要在常规应用程序启动时激活“dev”配置文件)。如果您不喜欢配置文件 - 将配置拆分为 3 个文件(applicationContext.xml、dev.xml、mock.xml)
    猜你喜欢
    • 2019-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-19
    • 2021-04-05
    • 1970-01-01
    相关资源
    最近更新 更多