Spring boot repository.save() does not work in test class - spring-boot

I have this test class:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = { CrimeServiceDBImpl.class, CrimeController.class, Crime.class })
#ComponentScan("com.springmiddleware")
#EntityScan(basePackages = {"com.springmiddleware.entities"})
#DataJpaTest
#AutoConfigureTestDatabase(replace = Replace.NONE)
#EnableJpaRepositories("com.springmiddleware")
public class TestCrimeServiceDB {
#Autowired
private CrimeServiceDBImpl service = new CrimeServiceDBImpl();
#Test
public void getAll() {
try {
List<Crime> list = this.service.getAllCrimes();
assertTrue(list.size()!=0);
} catch (IOException e) {
e.printStackTrace();
}
}
}
The method getAllCrimes() from the service class does just this:
#Service
public class CrimeServiceDBImpl implements CrimeService{
#Autowired
private CrimeRepository repository;
private List<Crime> list = new ArrayList<Crime>();
public CrimeServiceDBImpl() {
list = UtilityMethods.readFromCSV();
};
#Override
public List<Crime> getAllCrimes() throws IOException {
repository.saveAll(list);
return this.repository.findAll();
}
If I call this method when running the application, it correctly add all my objects to the database, but when it's called from the test it doesn't add anything, but no exception is thrown.

Which database are you using? Do you mean the data is not persisted in the database after the test has finished? That's because a test always perform a rollback/cleanup when it has finished its work.

Related

How to test a try...finally method only been called once in SpringBoot?

I am following this article to implement a database read/write separation feature by calling different methods. However, I got the error:
Missing method call for verify(mock) here: verify(spyDatabaseContextHolder, times(1)).set(DatabaseEnvironment.READONLY);
when doing the testing.
My test case is trying to verify DatabaseEnvironment.READONLY has been set once when using TransactionReadonlyAspect AOP annotation:
// TransactionReadonlyAspectTest.java
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {LoadServiceImpl.class, TransactionReadonlyAspect.class})
public class TransactionReadonlyAspectTest {
#Autowired
private TransactionReadonlyAspect transactionReadonlyAspect;
#MockBean
private LoadServiceImpl loadService;
#Test
public void testReadOnlyTransaction() throws Throwable {
ProceedingJoinPoint mockProceedingJoinPoint = mock(ProceedingJoinPoint.class);
Transactional mockTransactional = mock(Transactional.class);
DatabaseContextHolder spyDatabaseContextHolder = mock(DatabaseContextHolder.class);
when(mockTransactional.readOnly()).thenReturn(true);
when(loadService.findById(16)).thenReturn(null);
when(mockProceedingJoinPoint.proceed()).thenAnswer(invocation -> loadService.findById(16));
transactionReadonlyAspect.proceed(mockProceedingJoinPoint, mockTransactional);
verify(spyDatabaseContextHolder, times(1)).set(DatabaseEnvironment.READONLY); // got the error: Missing method call for verify(mock)
verify(loadService, times(1)).findById(16);
assertEquals(DatabaseContextHolder.getEnvironment(), DatabaseEnvironment.UPDATABLE);
}
}
//TransactionReadonlyAspect.java
#Aspect
#Component
#Order(0)
#Slf4j
public class TransactionReadonlyAspect {
#Around("#annotation(transactional)")
public Object proceed(ProceedingJoinPoint proceedingJoinPoint,
org.springframework.transaction.annotation.Transactional transactional) throws Throwable {
try {
if (transactional.readOnly()) {
log.info("Inside method " + proceedingJoinPoint.getSignature());
DatabaseContextHolder.set(DatabaseEnvironment.READONLY);
}
return proceedingJoinPoint.proceed();
} finally {
DatabaseContextHolder.reset();
}
}
}
// DatabaseContextHolder.java
public class DatabaseContextHolder {
private static final ThreadLocal<DatabaseEnvironment> CONTEXT = new ThreadLocal<>();
public static void set(DatabaseEnvironment databaseEnvironment) {
CONTEXT.set(databaseEnvironment);
}
public static DatabaseEnvironment getEnvironment() {
DatabaseEnvironment context = CONTEXT.get();
System.out.println("context: " + context);
return CONTEXT.get();
}
public static void reset() {
CONTEXT.set(DatabaseEnvironment.UPDATABLE);
}
}
//DatabaseEnvironment.java
public enum DatabaseEnvironment {
UPDATABLE,READONLY
}
// LoadServiceImpl.java
#Service
public class LoadServiceImpl implements LoadService {
#Override
#Transactional(readOnly = true)
public LoadEntity findById(Integer Id) {
return this.loadDAO.findById(Id);
}
...
}
I just want to test DatabaseContextHolder.set(DatabaseEnvironment.READONLY) has been used once then in the TransactionReadonlyAspect finally block it will be reset to DatabaseEnvironment.UPDATABLE which make sense.
However, how to test DatabaseContextHolder.set(DatabaseEnvironment.READONLY) gets called once? Why does this error occur? Is there a better way to test TransactionReadonlyAspect?

Spring batch + repository Testing

This is my reader that work in job and step i'u using a repository to get users(public interface UserRepository extends JpaRepository<User,Long>):
#Slf4j
public class Reader implements ItemReader<User> {
#Autowired
UserRepository userRepository;
private Iterator<User>userIterator;
#BeforeStep
public void before(StepExecution execution){
userIterator=userRepository.findAll().iterator();
}
#Override
public User read() {
if (userIterator != null && userIterator.hasNext()) {
User user=userIterator.next();
log.info("User-->"+user.toString());
return user;
} else {
return null;
}
}
}
This is my test class:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {SpringBatchApplication.class, BatchTestConfiguration.class})
public class SpringBatchApplicationTest {
#Autowired
private JobLauncherTestUtils testUtils;
#Autowired
private BatchConfig config;
#Test
public void testEntireJob() throws Exception {
final JobExecution result = testUtils.getJobLauncher().run(config.processJob(), testUtils.getUniqueJobParameters());
Assert.assertNotNull(result);
Assert.assertEquals(BatchStatus.COMPLETED, result.getStatus());
}
#Test
public void testSpecificStep() {
Assert.assertEquals(BatchStatus.COMPLETED, testUtils.launchStep("orderStep1").getStatus());
}
}
When i`m running my test i got a :
Caused by: java.lang.IllegalStateException: Cannot determine embedded database for tests. If you want an embedded database please put a supported one on the classpath.
What do i need to add to make determine of my database. Do i need to place application properties somewhere or something else?
There is how my test situate in project enter image description here

Cannot activate transaction for junit test

I want to test a transactional method. I added the #transactional over test method but I got an exception IllegalStateException: Failed to retrieve PlatformTransactionManager for #Transactional test. I don't know how to activate the transaction. I tried to add TestTransaction.start() in the first line of the test body but it throws an exception that the transaction is not active. I don't know what's wrong with my test.
If I remove classes in #SpringbootTest the test randomly throws NoSuchBeanException for my repositories.
My test method:
#ExtendWith(SpringExtension.class)
#DataJpaTest
#AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.H2)
class PersistenceHelperTest {
#Autowired
private ReturnRepository returnRepository;
#SpyBean
private PersistenceHelper persistenceHelper;
#Test
void removeFromDbOnWriteExceptionForNotWritableFile(#TempDir Path tempDir) {
Path testFile = tempDir.resolve("testFile.txt");
try {
String content = "This is a test";
testFile.toFile().createNewFile();
boolean writable = testFile.toFile().setWritable(false);
assertTrue("File should not be writable", writable);
persistenceHelper.saveToFileAndDB(content.getBytes(), testFile.toFile().getAbsolutePath(), returnFile, returnRepository);
fail();
} catch (Exception e) {
assertTrue("Should be instance of IOException", e instanceof IOException);
assertTrue("Should exists", Files.exists(testFile));
assertSame(0, returnRepository.findAll().size());
}
}
The class under the test:
#Component
public class PersistenceHelper {
#Transactional(rollbackFor = {IOException.class}, propagation = Propagation.REQUIRES_NEW)
public <T extends BaseEntity> void saveToFileAndDB(byte[] fileContent, String fileAbsolutePath, T entity, JpaRepository<T, Long> jpaRepository) throws IOException {
FileTransactionListener transactionListener = new FileTransactionListener(new FileDeleter(), fileAbsolutePath);
TransactionSynchronizationManager.registerSynchronization(transactionListener);
jpaRepository.save(entity);
FileUtil.writeToFile(fileAbsolutePath, fileContent);
}
}
my Springboot class:
#SpringBootApplication
#EnableTransactionManagement
#EnableJpaRepositories("packageAddress")
public class MyApplication {

Seperate liquibase file with test data per test class in Spring Boot

I want to seperate test data for particular test. For example we have two test classes A and B they are running on diffrent data so I want to create
ATestData.xml and BTestData.xml and when test class starts it should execute ATestData.xml then rollback then start test B execute BTestData.xml.
in Arquillian Persistence Extension there was #UsingDataSet("datasets/users.yml").
I started to wrtie my own annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE, ElementType.METHOD})
public #interface LiquibaseTestDataFile {
String value() default "";
}
And
#Slf4j
#Aspect
#Component
public class LiquibaseTestDataFileAspect {
#Before("execution(* (#LiquibaseTestDataFile *).*(..)) || execution(#LiquibaseTestDataFile * *(..))")
public void insertTestData(JoinPoint joinPoint){
log.info("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB");
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Class<?> declaringClass = signature.getMethod().getDeclaringClass();
LiquibaseTestDataFile myAnnotation = declaringClass.getAnnotation(LiquibaseTestDataFile.class);
//TODO if null maybe on method
//log.info(myAnnotation.value());
}
}
But it not trigers when annotation is on test class.
I am using #EnableAspectJAutoProxy in configuration
and
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {TransferManagerRestApplication.class})
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
in test class.
I have find solution by using #PostConstruct
#Component
public class LiquibaseDataTestConfiguration {
#Autowired
private SpringLiquibase springLiquibase;
public void insert(String fileName){
try {
Connection connection = springLiquibase.getDataSource().getConnection();
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection));
Liquibase liquibase = new liquibase.Liquibase(fileName, new ClassLoaderResourceAccessor(), database);
liquibase.update(new Contexts(springLiquibase.getContexts()), new LabelExpression(springLiquibase.getLabels()));
} catch (LiquibaseException | SQLException e) {
e.printStackTrace();
}
}
}
and in test class
#Autowired
private LiquibaseDataTestConfiguration liquibaseDataTestConfiguration;
#PostConstruct
public void initData(){
liquibaseDataTestConfiguration.insert("liquibase/AClassTest.xml");
}

Calling service layer directly from main class

I have a project structure controller -> response builder -> service layer.
Here service layer call the repository layer(database layer).
Everything is ok when follow the structure.
But,for testing i want to call the service layer directly from a java main class.
How can i do this??????
My controller:
#RestController
#RequestMapping("/ishmam")
public class IshmamAddressController {
#Autowired
#Qualifier("ishmamAddressBuilder")
IshmamAddressBuilder ishmamAddressBuilder;
#RequestMapping("/getAddress")
public IResponseDto<WebbCustomerAddressDto> getAllAddress(){
return ishmamAddressBuilder.getAllAddress();
}
}
My builder class is:
#Component("ishmamAddressBuilder")
public class IshmamAddressBuilder {
#Autowired
#Qualifier("ishmamAddressServiceImpl")
IshmamAddressInterface ishmamAddressService;
public IResponseDto<IshmamAddressResponseDto> getAllAddress(){
IResponseDto<WebbCustomerAddressDto> response=new
IResponseDto<WebbCustomerAddressDto>();
try{
//here i call the service layer
List<String> addressList=ishmamAddressService.getAllAddress();
}catch(Exception e){
throw e;
}
return addressList;
}
My service layer is:
#Service("ishmamAddressServiceImpl")
#Transactional
public class IshmamAddressServiceImpl implements IshmamAddressInterface {
#Autowired(required = true)
#Qualifier("webCustomerAddressRepository")
WebCustomerAddressRepository webCustomerAddressRepository;
#Override
public IshmamAddressResponseDto getAllAddress() {
List<WebCustomerAddress> aList = new ArrayList<WebCustomerAddress>();
List<WebbCustomerAddressDto> dtoWebCustomerAddressList = new
ArrayList<WebbCustomerAddressDto>();
IshmamAddressResponseDto ishmamAddressResponseDto=new
IshmamAddressResponseDto();
try{
aList =
address.getAllAddress(1);//Calling database layer
ishmamAddressResponseDto=//Doing something,not important for
//question
}
return ishmamAddressResponseDto;
}
Now what i want is to call the service layer directly from the main class:
public class Address{
public void getAddress(){
IshmamAddressServiceImpl i=new IshmamAddressServiceImpl();
List<String> list=i.getAllAddress();
}
public static void main(String[] args){
Address a=new Address();
a.getAddress();
}
}
This process is not working.How can i do this???????
As soon as you use spring, you must never use new to build an object that should be managed by spring.
So you could:
either do it manually, that means bootstrap an application context with the same intialization that it would have in your application and explicitely get a bean from it
ApplicationContext ctx = new AnnotationConfigApplicationContext(
Class<?>... annotatedClasses); // if it is the way you use it ...
IshmamAddressInterface i = ctx.getBean("ishmamAddressServiceImpl",
IshmamAddressInterface .class);
or use the Spring test framework that does it automatically for you in Junit tests
#WebAppConfiguration
#ContextConfiguration(classes = ...)
public class WebIntegrationTests {
#Autowired
#Qualifier("ishmamAddressServiceImpl")
IshmamAddressInterface ishmamAddressService;
...
#Test
public void testGetAddress() {
ishmamAddressService.getAddress();
...
}
}

Resources