10

Voglio testare il metodo save() della sessione di sospensione utilizzando il framework di test di primavera. metodo @Test è:Come svuotare i dati in db all'interno della transazione spring attiva?

@Test 
@Transactional 
public void testSave() { 
User expected = createUser(); 
getGenericDao().currentSession().save(expected); 
User actual = getUser(generatedId); 
assertUsersEqual(expected,actual); 
} 

Voglio irrigare utente nel database. Voglio che il mio utente di essere nel database dopo questo metodo

getGenericDao().currentSession().save(expected); 

poi voglio andare a database utilizzando framework di dati di primavera a prendere questa salvato utente riga successiva:

User actual = getUser(generatedId); 

ho cercato di usare Hibernate metodo flush come:

currentSession().setFlushMode(MANUAL); 
//do saving here 
currentSession().flush(); 

Non scarica l'utente nel database! Tuttavia, se non faccio uso dell'annotazione spring @Transactional e salvo il mio utente nella transazione programmatic spring ottengo quello che voglio. Sfortunatamente, l'utente salvato in db non è rollback in quanto non esiste una molla @Transactional. Pertanto il mio metodo di test cambia il db e il comportamento dei metodi di test successivi.

Quindi ho bisogno di scaricare il mio utente nel metodo di test db inside (non alla fine) e alla fine del rollback del metodo di test tutte le modifiche a db.

UPDATE suggerimento per preparare metodo come segue:

@Transactional 
public void doSave(User user){ 
    getGenericDao().currentSession().save(user); 
} 

e chiamare doSave all'interno testSave sta facendo nulla. Ancora non ho nessun utente in db dopo aver eseguito questo metodo. Ho impostato il breakpoint e controllo il mio db dalla riga di comando.

UPDATE Grazie mille per la risposta. Il problema è che il metodo flush() non inserisce il mio utente nel database. Ho provato Isolation.READ_UNCOMMITTED e non inserisce il mio utente nel database. Posso ottenere quello che voglio, ma solo se spengo la transazione primavera sul metodo @Test e faccio il salvataggio nella transazione programmatica. MA poi il metodo @Test non viene ripristinato lasciando l'utente salvato per i successivi metodi @Test. Qui il metodo @Test per salvare l'utente non è pericoloso quanto il metodo @Test per eliminare l'utente, perché non viene eseguito il rollback. Quindi deve esserci un supporto transazionale per il metodo @Test con il quale non posso comunque mettere il mio utente (o eliminare) in db. In realtà l'utente viene messo (o eliminato) in db solo dopo che il metodo @Test termina e viene eseguita la transazione per il metodo @Test. Quindi voglio salvare il mio utente in db nel mezzo del metodo @Test e ripristinarlo alla fine del metodo @Test

Grazie!

+0

Puoi darci anche la tua lezione di GenericDao? EDIT: in realtà, penso di vedere il problema. La transazione corrente deve essere confermata prima che i dati vengano effettivamente salvati nel database. Quindi sono necessari due metodi @Transactional: uno da salvare e un altro da caricare. –

+0

Guarda se ho fatto correttamente –

+0

Chiudi. Voglio dire avere due funzioni transazionali chiamate da una funzione non transazionale. Questo dovrebbe funzionare ... –

risposta

1

Se flush non funziona, dipende molto dal livello di isolamento del database.

Isolation è una delle proprietà del database ACID, che definisce come/quando le modifiche apportate da una operazione diventano visibili ad altre operazioni concorrenti.

Credo che il vostro livello di isolamento è impostato su Read Committed o Repeatable Read.

8

Infine ho attaccato alla seguente soluzione:

In primo luogo, i miei @Test metodi non sono in esecuzione all'interno di primavera @Transactional supporto. Vedi this article per sapere quanto pericoloso possa essere. Successivamente, anziché utilizzare i bean @Repository all'interno dei metodi @Test, autowire i bean @Service che utilizzano l'annotazione @Transactional. Il miracolo è che @Test metodo come questo

@Test 
@Transactional(propagation = Propagation.NOT_SUPPORTED) 
public void testSave() { 
    Answer created = createAnswer(); 
    Long generatedId = answerService.save(created); 
//at this moment answer is already in db 
    Answer actual=getAnswerById(generatedId); 
... } 

mette il mio oggetto risposta nel database (subito dopo answerService.save(created);) e il metodo getAnswerById va a DB e lo estrae per verificare se Salva era corretta.
Per eliminare le modifiche apportate al database in @Test metodo ricreo database JdbcTestUtils.executeSqlScript

+1

Invece di utilizzare JdbcTestUtils è possibile applicare l'annotazione @DirtiesContext. –

0

Si dovrebbe anche prendersi cura del importedpackage: nel mio caso ho importato

import javax.transaction.Transactional;

invece di

import org.springframework.transaction.annotation.Transactional;

1
  1. Date un'occhiata here con avviso circa @Transactional test (Spring Pitfalls: Transactional tests considered harmful). Ho usato @org.springframework.test.context.jdbc.Sql per ri-popolare DB nei miei test di servizio e @Transactional per i controller.
  2. ConstraintViolationException per il test di aggiornamento del controller con dati non validi sono stati lanciati solo al momento della transazione. Quindi ho trovato 3 opzioni:
    • 2.1 Annotazione test con @Commit o con @Transactional(propagation = Propagation.NEVER). Sii consapevole del cambiamento del DB.
    • 2.2 Utilizzo TestTransaction

Codice:

 TestTransaction.flagForCommit(); 
    TestTransaction.end(); 
  • 2,3 Usa TransactionTemplate

Codice:

@Autowired 
    private PlatformTransactionManager platformTransactionManager; 

    @Test(expected = Exception.class) 
    public void testUpdate() throws Exception { 
     TransactionTemplate transactionTemplate = new TransactionTemplate(platformTransactionManager); 
     transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); 
     String json = ... 
     transactionTemplate.execute(ts -> { 
      try { 
       mockMvc.perform(put(REST_URL + USER_ID) 
        .contentType(MediaType.APPLICATION_JSON) 
        .content(json)) 
        .andExpect(status().isOk()); 
       ... 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
      return null; 
     }); 
Problemi correlati