Spring boot test #Transactional not saving - java

I'am trying to do a simple Integration test using Spring Boot Test in order to test the e2e use case. My test does not work because I'am not able to make the repository saving data, I think I have a problem with spring contexts ...
This is my Entity:
#Entity
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Person {
#Id
private int id;
private String name;
}
This is the Person repository:
#Repository
public interface PersonRepository extends JpaRepository<Person, Integer> {
}
The Person service:
#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();
}
}
The Person Controller:
#RequestMapping
#RestController
public class PersonController {
#Autowired
private PersonService personService;
#RequestMapping("/persons")
public List<Person> getPersons() {
return personService.getPersons();
}
}
The main Application class:
#SpringBootApplication
public class BootIntegrationTestApplication {
public static void main(String[] args) {
SpringApplication.run(BootIntegrationTestApplication.class, args);
}
}
The application.properties file:
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
And the 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);
}
}
The test does not work, because the service is not saving the Person entity ....
Thanks in advance

#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", "x#x.com"));
}
}
Refer #Rollback(false) is key to avoid rollback.

Thanks to M. Deinum, I think I get the point,
So the best is to separate the logic of the test into two tests, the first will testing just the service (so this one could be transactional) and the second the controller:
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);
}

Spring for saving entity requires transaction. But until transaction has been commited changes not be visible from another transaction.
Simplest way is call controller after commit transaction
#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);
}
});
}

For each #Test function that makes a DB transaction, if you want to permanently persist the changes, then you can use #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);
}
I tested it with a SpringBoot project generated by jHipster:
SpringBoot: 1.5.4
jUnit 4.12
Spring 4.3.9

Pay your attention to the order in which the tests are executed, the tests with the #Commit or #Rollback(false) annotation must be executed first: https://www.baeldung.com/junit-5-test-order

Do not use #Rollback(false). Unit Test should not generate data.
JPA FlushMode is AUTO (default - flush INSERT/UPDATE/DELETE SQL when query occurs) / COMMIT.
Just query the working entity for forcing FLUSH, or using EntityManager to force flush
#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
}

Related

Spring Boot Test Mock a service method

How Can I mock the dep object without considering the internal logic of getDepartmetdDetails:
#Service
public class StudentService {
#Autowired private DepartmentService departmentService;
public List<Student> getDetails(String departmentId, int page, int size) {
.....
Department dep = departmentService.getDepartmetdDetails(departmentId);
}
...........
}
My tests are as follows.
#SpringBootTest
#EnableAutoConfiguration(
exclude = {
MongoAutoConfiguration.class,
MongoRepositoriesAutoConfiguration.class,
MongoDataAutoConfiguration.class
})
#AutoConfigureJsonTesters
#AutoConfigureMockMvc
#ActiveProfiles("unit-test")
public class StudentTest {
#InjectMocks
public DepartmentService departmentService;
#Test
void whengetDetails() throws Exception {
String departmentId = "departmentId";
.....
when(departmentService.getDepartmetdDetails(departmentId))
.thenReturn(department);
}
}
Here it will go inside the getDepartmetdDetails method in departmentService. I need to mock the result returning from the getDepartmetdDetails without going inside the method.

#DirtiesContext not clearing database

I have tried the following steps but no luck sadly :
Method number 1 always fail because size expected 1 but its 2 -> test runned individually passing through successfully but when i run all application tests then exactly that one is failing
1)Mark each method with #DirtiesContext(methodMode = DirtiesContext.MethodMode.BEFORE_METHOD)
2)Mark at class level #DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
3)Mark methods with #Transactional
#SpringBootTest
public class ExportServiceTest {
#Autowired
ExportService exportService;
#Autowired
ExportRepository exportRepository;
#Autowired
UserRepository userRepository;
#Test
public void onlyOneExportUserShouldBeAddedWithMultipleEntries() {
UserEntity userEntity = new UserEntity().setId(2L);
exportService.createIfNotExist(userEntity);
exportService.createIfNotExist(userEntity);
Assertions.assertThat(exportRepository.findAll()).hasSize(1);
}
#Test
public void exportUserShouldBeAdded() {
UserEntity userEntity = new UserEntity().setId(1L);
exportService.createIfNotExist(userEntity);
Optional<UserExportEntity> exportUser = exportRepository.findByUserEntityId(userEntity.getId());
Assertions.assertThat(exportUser).isPresent();
}
#Test
public void shouldHaveTwoRecordsInExport() {
UserEntity userEntity = new UserEntity().setId(1L).setEmail("test#test.com");
UserEntity userEntity2 = new UserEntity().setId(2L).setEmail("test1#test.com");
userRepository.save(userEntity);
userRepository.save(userEntity2);
exportService.createIfNotExist(userEntity2);
exportService.createIfNotExist(userEntity);
Assertions.assertThat(exportRepository.findAll()).hasSize(2);
}
}

Integration test db h2

Hello i want to make a integration test for my db in h2. But i have some problem with my method.
My integration test code :
#RunWith(SpringRunner.class)
#DataJpaTest
public class Team_database_integration_test {
#MockBean
private TeamRepository teamRepository;
#Autowired
private TestEntityManager testEntityManager;
#Test
public void testDb() {
Team team = new Team(1L, "teamName", "teamDescription", "krakow", 7);
testEntityManager.persist(team);
testEntityManager.flush();
Assert.assertEquals(1L, teamRepository.findById(team.getId()));
}
}
My error:
java.lang.AssertionError:
Expected :1
Actual :Optional.empty
I think a reason is in my service method findbyid:
public Optional<TeamDto> findTeamById(Long id) {
Assert.notNull(id, "ID must exist ");
return teamRepository
.findById(id)
.map(p -> modelMapper.map(p, TeamDto.class));
}

How to force transaction commit in Spring Boot test?

How can I force a transaction commit in Spring Boot (with Spring Data) while running a method and not after the method ?
I've read here that it should be possible with #Transactional(propagation = Propagation.REQUIRES_NEW) in another class but doesn't work for me.
Any hints? I'm using Spring Boot v1.5.2.RELEASE.
#RunWith(SpringRunner.class)
#SpringBootTest
public class CommitTest {
#Autowired
TestRepo repo;
#Transactional
#Commit
#Test
public void testCommit() {
repo.createPerson();
System.out.println("I want a commit here!");
// ...
System.out.println("Something after the commit...");
}
}
#Repository
public class TestRepo {
#Autowired
private PersonRepository personRepo;
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void createPerson() {
personRepo.save(new Person("test"));
}
}
Use the helper class org.springframework.test.context.transaction.TestTransaction (since Spring 4.1).
Tests are rolled back per default. To really commit one needs to do
// do something before the commit
TestTransaction.flagForCommit(); // need this, otherwise the next line does a rollback
TestTransaction.end();
TestTransaction.start();
// do something in new transaction
An approach would be to inject the TransactionTemplate in the test class, remove the #Transactional and #Commit and modify the test method to something like:
...
public class CommitTest {
#Autowired
TestRepo repo;
#Autowired
TransactionTemplate txTemplate;
#Test
public void testCommit() {
txTemplate.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
repo.createPerson();
// ...
}
});
// ...
System.out.println("Something after the commit...");
}
Or
new TransactionCallback<Person>() {
#Override
public Person doInTransaction(TransactionStatus status) {
// ...
return person
}
// ...
});
instead of the TransactionCallbackWithoutResult callback impl if you plan to add assertions to the person object that was just persisted.
Solutions with lambdas.
import java.lang.Runnable;
import java.util.function.Supplier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.support.TransactionTemplate;
#Autowired
TestRepo repo;
#Autowired
TransactionTemplate txTemplate;
private <T> T doInTransaction(Supplier<T> operation) {
return txTemplate.execute(status -> operation.get());
}
private void doInTransaction(Runnable operation) {
txTemplate.execute(status -> {
operation.run();
return null;
});
}
use as
Person saved = doInTransaction(() -> repo.save(buildPerson(...)));
doInTransaction(() -> repo.delete(person));

Testing JPA Entities

I want to know how to write Junit test for Spring Repository classes.
As example :
class-AccountMoveActionDet
Jpa interface-AccountMoveActionDetlJpaRepository
And I want to test this repository class work or not.Spring Jpa support some methods like
List findAll();
deleteAll();
I wrote a class just like below:
#RunWith(SpringJUnit4ClassRunner.class)
public class AccountTypeMovementJpaRepositoryTest extends AbstractJpaTest {
#Autowired
AccountTypeMovementJpaRepository accountTypeMovementJpaRepository;
#Override
public void test() {
executeSqlScript("/fixtures/accountTypeMovementJpa.sql");
assertEquals("accountTypeMovementJpaRepository Test", accountTypeMovementJpaRepository.findAll().size(),
JdbcTestUtils.countRowsInTable(getJdbcTemplate(), "COF5REP"));
}
}
Error creating bean with name
'com.gayan.cmp.jparepositories.test.AccountTypeMovementJpaRepositoryTest':
Please help me to resolve this.
If you use spring-boot 1.4 and above the best place to start, Testing the JPA slice :
#RunWith(SpringRunner.class)
#DataJpaTest
public class UserRepositoryTests {
#Autowired
private TestEntityManager entityManager;
#Autowired
private UserRepository repository;
#Test
public void findByUsernameShouldReturnUser() {
this.entityManager.persist(new User("sboot", "123"));
User user = this.repository.findByUsername("sboot");
assertThat(user.getUsername()).isEqualTo("sboot");
assertThat(user.getVin()).isEqualTo("123");
}
}

Categories

Resources