Why #Transactional also work in aop #Before method? - java

I have ProductService class in my project for Product CRUD
#Transactional
public Product registerNewProduct(ProductPayload productPayload) {
Product product = productPayload.getProduct();
PriceHistory priceHistory = productPayload.getPriceHistory();
if(priceHistory == null)
throw new PriceInfoNotExistException("Price info is needed to register new product.");
And before executing method, I'm trying to insert API call log info to my database using AOP #Before.
#Before("execution(com.example.demo.entity.Product com.example.demo.service.ProductService.*(..))")
public void beforeBeginTransaction(JoinPoint joinPoint) {
//some code here
productChangeHistoryRepository.saveAndFlush(productChangeHistory);
}
I'm testing CREATE operation without providing price info so I can get PriceInfoNotExistException.
But If PriceInfoNotExistException thrown, this code below executed but no data inserted.
productChangeHistoryRepository.saveAndFlush(productChangeHistory);
I think it's because of #Transactional annotation on registerNewProduct() method Because If I remove this annotation and test again without price info, log data successfully saves to database.
Is there any way that I can save some data in AOP #Before method while keeping the #Transactional annotation on my registerNewProduct()?

Spring will create a proxy method (where the execution of your #Before aspect code will be at first place) with all annotations the initial method have.
Not sure if AOP is a good choice to test the method. Try to mock different ProductPayload objects (with or without priceHistory) and call your method on it.

Related

Rollback not working on Spring Boot #Transactional method for H2 database

Please give me tips to get my failing JUnit test to pass in my Spring Boot app.
I have a Service that needs to delete rows from tables A and B (from a H2 database) in a transactional manner such that: if A is modified and then an exception occurs before B is modified, then neither A nor B are modified. However, even after 
Adding #Transactional to the PUBLIC Service::deleteRows method, and
Adding #EnableTransactionManagement to the #Configuration class that produces the JdbcTemplate that all repositories use, and
Confirming that spring-tx is on the classpath
the transaction still doesn't rollback.
Here's the failing JUnit testcase
// Service.java
#Transactional
public void deleteRows() {
aRepository.deleteRow(123);
bRepository.deleteRow(123);
}
// ServiceTest.java
#Test
void test() {​
// first ensure that an exception is thrown when bRepository.deleteRow() is called
// FYI deleteRow() calls jdbcTemplate.update()
    doThrow(RuntimeException.class)
        .when(mockJdbcTemplate)
        .update(any(), eq(123));
    BRepository mockBRepository = new BRepository(mockJdbcTemplate);
    Service service = new Service(realARepository, mockBRepository);
    assertTableHasRows(1, "TABLE_A"); // before calling the service, TABLE_A has 1 row
    assertThrows(RuntimeException.class, service::deleteRows);    
    assertTableHasRows(1, "TABLE_A"); // TABLE_A should still have 1 row when an exception occurs during the tx
}​
Remove #Transactional and run test again.
In the end the problem was solved by NOT using mocking to trigger an exception. Instead, we set up the database schema so that normal operation of service::deleteRows would trigger an exception.

How to execute sql script before and after each test method

There is an #Sql annotation in spring which allows to execute sql code before and after the test method:
#Test
#Sql("init.sql")
#Sql(scripts = "clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void someTest()
{
}
However I have several test methods where I would like to provide the same clean environment like in the test above and I don't want to repeat for every test the same #Sql annotation. How to do it once for all methods? For example:
// JPA and Spring other test annotations
#Sql("init.sql")
#Sql(scripts = "clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public class TestClass
{
// init.sql executed before every test, clean.sql executed after every test
}
Indeed when you place #Sql on the class, sql scripts will be executed before and after every test defined in that class, more specifically before #Before and after #After methods. So,
// JPA and Spring other test annotations
#Sql("init.sql")
#Sql(scripts = "clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public class TestClass
{
// init.sql executed before every test, clean.sql executed after every test
}
is going to work according to the #Sql definition:
#Target({ElementType.TYPE, ElementType.METHOD})
/// other annotations
public #interface Sql {
//
}
If my understanding is correct, you want to execute an init script to put the DB in a certain state, and then ensure the DB is back to that state before each test method, right?
The simplest solution is to use #Transactional, then. By default, Spring Boot will automatically roll back the test transaction of a #Transactional-annotated test, thus resetting the DB to the original state.
There are two downsides one should consider, though:
#Transactional means there will exist a transaction spanning the entire execution of the test method which the tested service methods will typically join. Hence, the test itself cannot be relied upon to verify the correctess of transactional boundaries in the production code (LazyInitializationExceptions may be covered by this 'outer' transacion, for example)
The persistence context will not flush unless necessary, meaning that some issues (e.g. DB constraint violations) will not surface. I tend to work around that issue using a last-chance flush like so:
#After
public void flushContext() {
if (TransactionSynchronizationManager.isActualTransactionActive()) {
entityManager.flush();
}
}

How to cleanup h2 db after each junit test?

I am making junit tests which adds some users before each test case. The code is:
#BeforeEach
void setUp() {
saveUser();
saveEntry();
}
#Test
void saveUser() {
User user = new User();
user.setUserId(null);
user.setUsername("John");
user.setEmail("john#foo.com");
user.setPassword("password");
userService.saveUser(user);
}
#Test
void saveEntry() {
Entry entry = new Entry();
entry.setText("test text");
entry.setUserId(1L);
entryService.saveEntry(entry);
}
As you see I am using the methods that I have in my service layer to create entries and users. If I run the tests one by one there is no problem. But when I run all tests then db is not returning 1 item and returning multiple items so exception occurs.
I need to cleanup h2 db after each test with maybe #AfterEach annotation but I do not have and delete method in my code to invoke. How can I cleanup the H2 db ?
In addition to #J Asgarov answer which is correct providing you use spring-boot if you want to perform some actions before and after each test (more specifically before #Before and after #After methods) you can use #Sql annotation to execute specific sql script for example from test resources.
#Sql("init.sql")
#Sql(scripts = "clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public class TestClass
}
It might be handy for sequences, since they don't care of rollback.
Regarding #Transactional mentioned by #Mark Bramnik be careful, because the transaction spans for entire test method, so you cannot verify correctness of the transaction boundaries.
You've mentioned spring and it looks like you're testing DAO layer, so I assume the tests are running with SpringExtension/SpringRunner if you're on junit 4.
In this case,
Have you tried to use #Transactional on the test method? Or alternatively if all the test methods are "transactional" you can place it once on a test class.
This works as follows:
If everything is configured correctly spring will open a transaction before the test starts, and will rollback that transaction after the test ends.
The rollback is supposed to clean the inserted data automatically.
Quick googling revealed this tutorial, surely there are many others
#JpaDataTest annotating the test class will rollback every test by default
https://www.baeldung.com/spring-jpa-test-in-memory-database

Java, Spring: Testing DAOs for DataAccessException with Mockito

I'm trying to increase my test coverage so I'm wondering, how would you go about testing for DataAccessExceptions being thrown in a DAO, for example in a simple findAll method which just returns all the data from your data source? In my case, I'm using Spring JdbcTemplates.
For general testing I have my setUp-method with an #Before annotation, mocking the jdbcTemplate used, setting it up in the DAO and mocking all jdbc calls. Now forcing a DataAccessException for something like a create method is pretty simple, just throw the exception when calling a create statement with the right primary keys.
However, I really have no idea how to handle this for methods like simple findAll methods which don't take any input parameters. Testing the valid implementation is straight forward, but how would you go about mocking having no DB connection without it affecting every other test or method?
This would be a concrete implementation of a method I'd like to test:
public List<SomeObject> findAll() throws PersistenceException {
final String sql = "SELECT * FROM SomeObject";
try {
return jdbcTemplate.query(sql, new JdbcSomeObjectMapper());
} catch (DataAccessException ex) {
LOG.error(ex.getMessage());
throw new PersistenceException(ex.getMessage());
}
}
Which would just return all objects in the data source. Testing for a valid call is easy since I can just mock the jdbcTemplate.query call, but I'd never enter the catch block unless there's a connection failure while retrieving the data, and that's what I'd like to test.
Using Mockito you can mock a Class and the method calls of that particular Class. A mocked object can also be asked to throw an exception when particular methods are called on it. First you have to mock your jdbcTemplate, then stub your exception
//mocking JdbcTemplate
JdbcTemplate template = Mockito.mock(JdbcTemplate.class);
Mockito.when(template.query(Mockito.anyString(), (RowMapper<YourClass>) Mockito.any(RowMapper.class))).thenThrow(EmptyResultDataAccessException.class);
//or using EasyMock
EasyMock.expect(template.query(Mockito.anyString(), (RowMapper<YourClass>) Mockito.any(RowMapper.class))).andThrow(new (typeofExecption));

transactional method throwing "no transactional entityManager available"

I have a method that is defined as #transactional. In fact I have a method calling a method that calls a method and all three are #transactional. The transactional logic worked fine, until I pulled a few methods out into an abstract class for some code reuse, which appears to have broken my logic somehow.
The transactional method is from an abstract class, here is a partial snippet of the relevant parts (I have to rewrite this by hand so forgive me for typos):
public abstract class ReadWriteService<ReadEntityTempalte extends IEntity, WriteEntityTemplate extends IEntity>
//extends jpaRepository, created using #enableJpaRepositories
private searchRepository<WriteEntityTemplate, String> writeRepository;
#PersistenceContext
private EntityManager em;
#transactional
public ReadEntityTemplate save(final WriteEntityTemplate entity){
if(entity == null) return null;
WriteEntityTemplate returnValue = writeRepository_.save(entity);
postSave(returnValue); //checks our security logic
flush();
ReadEntityTemplate returnEntity = find(returnValue.getId());
//required to detect changes made to the view by our save
em.refresh(returnEntity);
}
It's written this way because we are using views so the return value may be modified in the find() to the view. This logic worked in the past, and still works for a number of calls.
The method that fails is:
#Override
#transational
public void configure(EntityFileConfig config) throws ClassNotFoundException{
//load config from file
for(EntityConfig entityConfig: entityConfigs){
EntityType entityType=EntityTypeService_.find(entityConfig.getKey());
if(entityType==null){
entityType = EntityType.createByRequiredFields(entityConfig.getKey());
}
//update entityType to reflect config file.
entityType = entityTypeService_.save(entityType);
for(String permissionName: entityConfig.getPermissions()){
if(!entityTypeService_.hasPermission(entityType, permissionName)){
Permission permission = permissionSetup.getPermission(permissionName);
if(permission!=null)
//fails on below lines
permissionService._.addPermission(entityType, permission);
}
}
}
}
both the entityTypeService and the permissionService extend the above abstract class and use the same save method without alteration, addPermissions is a forloop that calls save on each permission.
The entityTypeService works, but the permissionService fails. When The permission service is called if I do em.isTransactionalEntity it returns false.
All #transactional annotations are using the spring annotation, not the javax one.
Actually, it seems as if a few of the permissions would save and others wouldn't, almost as if it's non-deterministic, but this may simple be due to modifying a database file that had some of the values already set and thus didn't need to run some of the logic the first time through.
I've done quite a bit of stumbling around but am no closer to determining what would cause my transaction to end. I had thought perhaps it was the #persistenceContext, since the JPARepos get their entityManager through a different approach then autowireing with #persistenceContext, but if that were the case everything would fail?
Any help would be appreciated, I'm pretty stumped on the cause of this.
Assuming you have enabled #EnableTransactionManagement on #Configuration class.
Since you didn't set any propagation on #Transaction the default value is Required. It means all methods must be part of transaction. Since one of your abstract methods is not part of the #Transactional hence the error.
For more information on Spring Transactions.
Note: Image taken from above link.

Categories

Resources