2012-02-09 7 views
10

Sto sviluppando un'applicazione Java con molte query di criteri Hibernate complesse. Vorrei testare questi criteri per assicurarmi che stiano selezionando gli oggetti giusti e solo quelli giusti. Un approccio a questo, ovviamente, consiste nell'impostare un database in memoria (ad esempio HSQL) e, in ciascun test, effettuare un round trip su quel database utilizzando i criteri e quindi asserire che i risultati della query corrispondono alle mie aspettative.Come testare le query dei criteri di ibernazione senza utilizzare alcun database?

Ma sto cercando una soluzione più semplice, poiché i criteri di Hibernate sono solo un tipo speciale di predicati logici sugli oggetti Java. In teoria, quindi, potrebbero essere testati senza accedere a nessun database. Per esempio, supponendo che ci sia un'entità chiamata Cat:

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

Vorrei fare qualcosa di simile, per creare criteri di query:

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

I criteri potrebbero essere utilizzati nel codice di produzione come questo :

C'è qualche libreria Java che rende possibile questo tipo di test?

risposta

15

È possibile utilizzare una struttura di simulazione come Mockito per prendere in giro tutte le classi rilevanti di Hibernate e definire il comportamento previsto di questi mock.

Suona come un sacco di codice, ma dal momento che il Hibernate Criteria API è un interfaccia fluida, tutti i metodi di Criteria restituire una nuova istanza Criteria. Quindi definire il comportamento simulato che è comune a tutti i test è semplice. Ecco un esempio di utilizzo 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); 
} 

Tutti i metodi del ritorno finto Criteria di nuovo il finto.

In un test concreto si dovrebbe quindi utilizzare un ArgumentCaptor e verify dichiarazioni di indagare quello che è successo alla deriso 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()); 
} 

Il problema che vedo con questo tipo di unitests è che essi impongono un sacco di aspettative circa la classe in prova. Se pensi a serviceUnderTest come una scatola nera, non puoi sapere come recupera l'oggetto gatto per nome. Potrebbe anche utilizzare un criterio LIKE o anche "IN" anziché =, inoltre potrebbe utilizzare il criterio Example. O potrebbe eseguire una query SQL nativa.

+4

Grazie per la risposta! Ma, come fai notare, questo tipo di test verifica come la classe è * implementata *, non come dovrebbe * comportarsi *. Inoltre, questo test è focalizzato su un altro servizio, che capita di utilizzare i criteri. Sebbene questa sia certamente una parte importante della funzionalità, dovrebbe essere relativamente facile da testare (separando le classi con il giusto uso di interfacce, separazione delle responsabilità, ecc.). Qui, sono più interessato a testare l'oggetto Criteria stesso. –

Problemi correlati