web-dev-qa-db-fra.com

Test de démarrage de printemps @Transactional non enregistré

J'essaie de faire un test d'intégration simple en utilisant Spring Boot Test afin de tester le cas d'utilisation e2e. Mon test ne fonctionne pas car je ne suis pas en mesure de faire enregistrer les données du référentiel, je pense que j'ai un problème avec les contextes de printemps ...

Ceci est mon entité:

@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    @Id
    private int id;
    private String name;
}

Voici le référentiel des personnes:

@Repository
public interface PersonRepository extends JpaRepository<Person, Integer> {
}

Le service Personne:

@Service
public class PersonService {

    @Autowired
    private PersonRepository repository;

    public Person createPerson(int id,String name) {
       return repository.save(new Person(id, name));
    }

    public List<Person> getPersons() {
      return repository.findAll();
    }
}

Le contrôleur de personne:

@RequestMapping
@RestController
public class PersonController {

  @Autowired
  private PersonService personService;

  @RequestMapping("/persons")
  public List<Person> getPersons() {
      return personService.getPersons();
  }

}

La classe d'application principale:

@SpringBootApplication
public class BootIntegrationTestApplication {

  public static void main(String[] args) {
    SpringApplication.run(BootIntegrationTestApplication.class, args);
  }
}

Le fichier application.properties:

spring.datasource.url= jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true

Et le test:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class BootIntegrationTestApplicationTests {

    @Autowired
    private PersonService personService;
    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    @Transactional
    public void contextLoads() {
        Person person = personService.createPerson(1, "person1");
        Assert.assertNotNull(person);

        ResponseEntity<Person[]> persons = restTemplate.getForEntity("/persons", Person[].class);
    }
}

Le test ne fonctionne pas, car le service ne sauvegarde pas l'entité Personne .... Merci d'avance

8
Hedi Ayed
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
public class SmokeTest {

    @Autowired
    UserController userController;

    @Autowired
    UserDao userDAO;

    @Rollback(false) // This is key to avoid rollback.
    @Test   
    public void contextLoads() throws Exception {
        System.out.println("Hiren");

        System.out.println("started");
        userDAO.save(new User("tyx", "[email protected]"));
    }
}

Reportez-vous à @Rollback(false) est la clé pour éviter un retour en arrière.

5
user2758406

Merci à M. Deinum, je pense avoir compris, donc le mieux est de séparer la logique du test en deux tests, le premier testera juste le service (donc celui-ci pourrait être transactionnel) et le second le contrôleur:

Test 1:

@Test
@Transactional
public void testServiceSaveAndRead() {
    personService.createPerson(1, "person1");
    Assert.assertTrue(personService.getPersons().size() == 1);
}

Test 2:

@MockBean
private PersonService personService;

@Before
public void setUp() {
    //mock the service
    given(personService.getPersons())
            .willReturn(Collections.singletonList(new Person(1, "p1")));
}

@Test
public void testController() {
    ResponseEntity<Person[]> persons = restTemplate.getForEntity("/persons", Person[].class);
    Assert.assertTrue(persons.getBody()!=null && persons.getBody().length == 1);
}

J'espère que cela aidera quelqu'un un jour ... merci pour vous tous

3
Hedi Ayed

Pour chaque fonction @Test Qui effectue une transaction de base de données, si vous souhaitez conserver de façon permanente les modifications, vous pouvez utiliser @Rollback(false)

@Rollback(false)
@Test
public void createPerson() throws Exception {
    int databaseSizeBeforeCreate = personRepository.findAll().size();

    // Create the Person
    restPersonMockMvc.perform(post("/api/people")
        .contentType(TestUtil.APPLICATION_JSON_UTF8)
        .content(TestUtil.convertObjectToJsonBytes(person)))
        .andExpect(status().isCreated());

    // Validate the Person in the database
    List<Person> personList = personRepository.findAll();
    assertThat(personList).hasSize(databaseSizeBeforeCreate + 1);
    Person testPerson = personList.get(personList.size() - 1);
    assertThat(testPerson.getFirstName()).isEqualTo(DEFAULT_FIRST_NAME);
    assertThat(testPerson.getLastName()).isEqualTo(DEFAULT_LAST_NAME);
    assertThat(testPerson.getAge()).isEqualTo(DEFAULT_AGE);
    assertThat(testPerson.getCity()).isEqualTo(DEFAULT_CITY);
}

Je l'ai testé avec un projet SpringBoot généré par jHipster:

  • SpringBoot: 1.5.4
  • jUnit 4.12
  • Printemps 4.3.9
1
Eduardo Toural

N'utilisez pas @Rollback(false). Le test unitaire ne doit pas générer de données.

JPA FlushMode est AUTO (par défaut - flush INSERT/UPDATE/DELETE SQL lorsque la requête se produit)/COMMIT.

Il suffit de demander à l'entité de travail de forcer FLUSH ou d'utiliser EntityManager pour forcer le vidage

@Test
public void testCreate(){
    InvoiceRange range = service.createInvoiceRange(1, InvoiceRangeCreate.builder()
            .form("01GTKT0/010")
            .serial("NV/18E")
            .effectiveDate(LocalDate.now())
            .rangeFrom(1L)
            .rangeTo(1000L)
            .build(), new byte[] {1,2,3,4,5});

    service.findByCriteria(1, "01GTKT0/010", "NV/18E");  // force flush
    // em.flush(); // another way is using entityManager for force flush
}
0
Neo Pham

Le printemps pour enregistrer l'entité nécessite une transaction. Mais tant que la transaction n'a pas été validée, les modifications ne sont pas visibles depuis une autre transaction.

Le moyen le plus simple est d'appeler le contrôleur après une transaction de validation

@Test
@Transactional
public void contextLoads() {
    Person person = personService.createPerson(1, "person1");
    Assert.assertNotNull(person);

    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
        @Override
        public void afterCommit() {
            ResponseEntity<Person[]> persons = restTemplate.getForEntity("/persons", Person[].class);
        }
    });        
}
0
Nick Savenia