如何在不使用任何数据库的情况下测试Hibernate条件查询?

Ota*_*edo 10 unit-testing hibernate-criteria

我正在开发一个包含大量复杂Hibernate标准查询的Java应用程序.我想测试这些标准,以确保他们选择正确的,只有正确的对象.当然,其中一种方法是建立内存数据库(例如HSQL),并在每次测试中使用条件对该数据库进行往返,然后断言查询结果符合我的期望.

但我正在寻找一个更简单的解决方案,因为Hibernate标准只是一种关于Java对象的特殊逻辑谓词.因此,理论上,它们可以在不访问任何数据库的情况下进行测试.例如,假设有一个名为的实体Cat:

class Cat {
    Cat(String name, Integer age){
        this.name = name;
        this.age = age;
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

我想做这样的事情来创建条件查询:

InMemoryCriteria criteria = InMemoryCriteria.forClass(Cat.class)
   .add(Restrictions.like("name", "Fritz%"))
   .add(Restrictions.or(
      Restrictions.eq("age", new Integer(0)),
      Restrictions.isNull("age")))

assertTrue(criteria.apply(new Cat("Foo", 0)))
assertTrue(criteria.apply(new Cat("Fritz Lang", 12)))
assertFalse(criteria.apply(new Cat("Foo", 12)))
Run Code Online (Sandbox Code Playgroud)

标准可以在生产代码中使用,如下所示:

criteria.getExecutableCriteria(session); //similar to DetachedCriteria
Run Code Online (Sandbox Code Playgroud)

是否有任何Java库可以进行此类测试?

rai*_*198 15

您可以使用像Mockito这样的模拟框架来模拟所有相关的Hibernate类并定义这些模拟的预期行为.

听起来很多代码,但由于Hibernate Criteria API是一个流畅的接口,所有Criteria返回新实例的方法Criteria.因此,定义所有测试通用的模拟行为很简单.以下是使用Mockito的示例

@Mock
private SessionFactory sessionFactory;

@Mock
Session session;

@Mock
Criteria criteria;

CatDao serviceUnderTest;

@Before
public void before()
{
    reset(sessionFactory, session, criteria);
    when(sessionFactory.getCurrentSession()).thenReturn(session);
    when(session.createCriteria(Cat.class)).thenReturn(criteria);
     when(criteria.setFetchMode(anyString(), (FetchMode) anyObject())).thenReturn(criteria);
    when(criteria.setFirstResult(anyInt())).thenReturn(criteria);
    when(criteria.setMaxResults(anyInt())).thenReturn(criteria);
    when(criteria.createAlias(anyString(), anyString())).thenReturn(criteria);
    when(criteria.add((Criterion) anyObject())).thenReturn(criteria);

    serviceUnderTest = new CatDao(sessionFactory);
}
Run Code Online (Sandbox Code Playgroud)

Criteria模拟的所有方法再次返回模拟.

在具体测试中,您将使用ArgumentCaptorverify语句来调查被模拟的内容Criteria.

@Test
public void testGetCatByName()
{
    ArgumentCaptor<Criterion> captor = ArgumentCaptor.forClass(Criterion.class);

    serviceUnderTest.getCatByName("Tom");

    // ensure a call to criteria.add and record the argument the method call had
    verify(criteria).add(captor.capture());

    Criterion criterion = captor.getValue();

    Criterion expectation = Restrictions.eq("name", "Tom");

    // toString() because two instances seem never two be equal
    assertEquals(expectation.toString(), criterion.toString());
}
Run Code Online (Sandbox Code Playgroud)

我看到这种不同的问题是他们对被测试的课程提出了很多期望.如果您将其serviceUnderTest 视为黑盒,则无法知道它是如何通过名称检索cat对象的.它也可以使用LIKE标准甚至"IN"代替=,进一步使用 Example标准.或者它可以执行本机SQL查询.

  • 谢谢你的回答!但是,正如您所指出的,这种测试验证了类的实现方式*,而不是它应该如何*表现*.此外,此测试专注于其他服务,恰好使用标准.虽然这肯定是一个重要的功能,但它应该相对容易测试(通过正确使用接口,责任分离等来解耦类).在这里,我更感兴趣的是测试Criteria对象本身. (4认同)