Как тестировать запросы к спящим критериям без использования какой-либо базы данных?

Я разрабатываю Java-приложение с множеством сложных запросов к спящим критериям. Я бы хотел проверить эти критерии, чтобы убедиться, что они выбирают правильные и только правильные объекты. Один из подходов к этому, конечно, состоит в том, чтобы создать базу данных в памяти (например, HSQL) и в каждом тесте совершить кругосветное путешествие к этой базе данных с использованием критериев, а затем утверждать, что результаты запроса соответствуют моим ожиданиям.

Но я ищу более простое решение, поскольку критерии Hibernate - это всего лишь особый вид логических предикатов о объектах Java. Таким образом, теоретически они могли быть протестированы без какого-либо доступа к какой-либо базе данных. Например, если предположить, что существует объект под названием Cat:

class Cat {
    Cat(String name, Integer age){
        this.name = name;
        this.age = age;
    }
    ...
}

Я хотел бы сделать что-то вроде этого, чтобы создать критерии запросов:

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)))

Критерии могут быть использованы в производственном коде, например:

criteria.getExecutableCriteria(session); //similar to DetachedCriteria

Есть ли какая-либо библиотека Java, которая делает такой тип тестирования возможным?

Ответ 1

Вы можете использовать насмешливую инфраструктуру, такую как Mockito, чтобы высмеять все соответствующие классы Hibernate и определить ожидаемое поведение этих макетов.

Звучит как много кода, но поскольку API-интерфейс Hibernate Criteria - это свободный интерфейс, все методы 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);
}

Все методы " Criteria возвращаются обратно.

В конкретном тесте вы затем используете ArgumentCaptor и verify утверждения, чтобы выяснить, что произошло с издеваемыми 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());
}

Проблема, которую я вижу в этом виде унитатов, заключается в том, что они налагают много ожиданий на тестируемый класс. Если вы считаете, что serviceUnderTest как serviceUnderTest, вы не можете знать, как он возвращает объект cat по имени. Он также может использовать критерий LIKE или даже "IN" вместо =, в дальнейшем он может использовать критерий " Example. Или он может выполнить собственный SQL-запрос.