maven-surefire-plugin runs single method, but failed on class - spring-boot

I wrote test that require transactions, it looks like :
#RunWith(SpringRunner.class)
#SpringBootTest(classes = ExchangeApp.class)
#EnableTransactionManagement(proxyTargetClass = true, mode = AdviceMode.PROXY)
#ActiveProfiles({JHipsterConstants.SPRING_PROFILE_TEST})
public abstract class AbstractServiceTest {
So when I run single test method : mvn test -Dtest=TestClassName#method1
works as expected, but
mvn test -Dtest=TestClassName
failed, with weird exceptions, exception says constraint violation in #OneToMany and exceptions with decimal calculations during divide in BigDecimal. Same exceptions when I run in IDE.
It looks like transaction managing missed. Any ideas ?
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["FK_OPEN_EXEC_ID: PUBLIC.ORDER_PAIR_OPEN_EXEC FOREIGN KEY(EXECUTIONS_ID) REFERENCES PUBLIC.ORDER_PAIR_OPEN(ID) (2)"; SQL statement:
delete from order_pair_open where id=? [23503-197]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
UPD: also I already tried
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<!-- Force alphabetical order to have a reproducible build -->
<runOrder>alphabetical</runOrder>
<parallel>classes</parallel>
<threadCountClasses>1</threadCountClasses>
<threadCountMethods>1</threadCountMethods>
<threadCountSuites>1</threadCountSuites>
</configuration>
</plugin>
UPD: it is specific to my case. I am trying to test service with #Async method inside, so it seems I have to mark #Transactional on test method name, in order to enable transaction support, thats why I tried to use #EnableTransactionManagement(proxyTargetClass = true, mode = AdviceMode.PROXY) to enable transactions managing during class test. Here it is pseudo code :
class Service1Test extends AbstractServiceTest {
Service1 service1;
Repo1 repo1;
//Does not works with class call, but works with method call
//when I mark this method with #Transactional, mentioned exceptions are gone,
// but I cant check result since "registerSynchronization" were not called
#Test
public void test1() throws InterruptedException {
service1.method1();
synchronized (this) {
wait(2000l);
}
assertThat( repo1.findAll().size()).isEqualTo(1);
//repoN check
}
}
#Service
#Transactional
class Service1 {
Service2 service2;
#Async
public void method1() {
//DB operations...
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
#Override
public void afterCommit() {
service2.method2();
}
});
}
}
#Service
class Service2 {
Repo1 repo1;
public void method2() {
repo1.save(new Entity1());
}
}
#Service
class Service3 {
#Autowired
private ScheduledExecutorService scheduler;
public void method3() {
scheduler.schedule(() -> {
//other transactional services call
}, 1l, TimeUnit.SECONDS);
}
}
#Repository
interface Repo1 extends JpaRepository<Entity1, Long> {
}
#Entity
class Entity1{
}

I can't comment on JHipster side, but one possible reason is that "Test Transactional" behavior is not applied to the test code.
Just to clarify, by default if you make a test as #Transactional, spring opens one transaction, the test runs, and when its done (regardless whether it has passed or failed) the transaction does the rollback effectively cleaning up the database.
Now, I don't see this in tests, so probably you don't use this behavior.
But this is pure spring, not a spring boot.
Now regarding the spring boot part.
If you use #SpringBootTest with a concrete configuration ExchangeAppin this case, the chances are that it won't load any autoconfiguration-s (for example those that define configurations working with transactions, data source management, etc.).
If you want to "mimic" the load of the microservice, you should run #SpringBootTest without configurations but it's beyond the scope of the question.
A "by the book" spring boot way to test DAO with hibernate is using #DataJpaTest that loads only the database related stuff, but it can't be used with #SpringBootTest - you should choose one.
So for me it's clear that the test does something tricky and definitely not something that follows spring boot conventions, so probably spring/spring boot strikes back :)
Now, regarding the Asynchronous stuff. This can also contribute to the mess because transaction support in spring relies heavily on Thread Local concept, so when the new thread gets executed (on another thread pool or something) the information about transaction does not propagate, so spring can't understand that its still in the same transaction. I see that you use the TransactionSynchronizationManager but without debugging its hard to tell what happens.
Now in order to check why doesn't the transaction get propagated, I think you should debug the application and see:
Whether the services are wrapped in proxy that support transactions (that's what #Transactional does, assuming the relevant BeanPostProcessor was applied)
Check that during each step you're in the transaction
Consider using #Transactional on test / test case, so that it would clean up the changes that have been applied during the test

Related

spring unit test repository save not working

I'm trying to write a unit test, for my spring server.
First it will check if a username is present or not in repository, if not so the username is available, then it will return true, and then I will save that username to my repository and check if available ot not it should return false.
Here is my code:
#Test
public void availableTest() {
String username="some_username";
LoginCredential lc=new LoginCredential();
lc.setUsername(username);
lc.setPasswordHash("1");
lc.setSessionID(0);
assertEquals(true, loginCredentialService.available(username));
loginCredentialRepository.save(lc);
assertEquals(false, loginCredentialService.available(username));
}
But for some reason, for the last assertEquals it gives me error. So I can say, the data is not being saved in repository, as I have tested my APIs using postman.
So how this can be resolved?
I think testing class is properly annotated:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MainApplicationTests {
#Autowired
private LoginCredentialService loginCredentialService;
#MockBean
private LoginCredentialRepository loginCredentialRepository;
...
You tagged the OP with H2, so I guess you know about In-Memory databases.
If you use the #SpringBootTest annotation, you are writing an integration test, so you'll test the full application as wired by Spring. For efficiency you might want to use an in-memory database for testing instead of a full SQL Server.
You can achieve this by adding H2 database as a test dependency, which will be picked up by Spring Boot for integration test repositories. Then you can inject the actual repository:
#Autowired
private LoginCredentialRepository loginCredentialRepository;
Additionaly, you can make your test #Transactional. Then every test case will run in a separate transaction, and the transaction will be rolled back after every test, so you don't need to worry about cross-test polution.
If you just want to Unit Test the LoginCredentialService, you need to stub the relevant methods on the repository, e.g.
#MockBean
private LoginCredentialRepository loginCredentialRepository;
#Test
public void availableTest1() {
when(loginCredentialRepository.existByName(username)).thenReturn(true);
assertEquals(false, loginCredentialService.available(username));
}
#Test
public void availableTest2() {
when(loginCredentialRepository.existByName(username)).thenReturn(false);
assertEquals(true, loginCredentialService.available(username));
}
You can do this as a pure Mockito test, too, without #SpringBootTest.

Spring H2 Test DB does not reset before each test

EDIT: As C. Weber suggested in the comments, the solution is to add #Transactional to the test class.
I have some tests that use an H2 in-memory DB. I need to reset the DB before each test. Although my SQL scripts are run each a test is executed, the DB is not properly reset, resulting in a missing needed entry after a delete test.
Test class:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureTestDatabase(replace=Replace.ANY, connection=EmbeddedDatabaseConnection.H2)
public class RepositoryTests {
#Autowired
private Repository repository;
#Autowired
private DataSource dataSource;
#Before
public void populateDb() {
Resource initSchema = new ClassPathResource("database/schema.sql");
Resource initData = new ClassPathResource("database/data.sql");
DatabasePopulator dbPopulator = new ResourceDatabasePopulator(initSchema, initData);
DatabasePopulatorUtils.execute(dbPopulator, dataSource);
}
#Test
public void testMethod1() {
// ...
repository.delete("testdata");
}
#Test
public void testMethod2() {
// ...
Object test = repository.get("testdata");
// is null but should be an instance
}
}
schema.sql drops all tables before recreating them. data.sql inserts all needed test data into the DB.
Running the testMethod2 alone succeeds. However, running all tests makes the test fail with a NullPointerException.
I have successfully tried to use #DirtiesContext, however this is not an option because I can't afford to have a 20 second startup for each 0.1 second test.
Is there another solution?
The Spring Test Framework provides a mechanism for the behaviour you want for your tests. Simply annotate your Test class with #Transactional to get the default rollback behaviour for each test method.
There are ways to configure the transactional behaviour of tests and also some pitfalls (like using RestTemplate inside test method), which you can read more about in the corresponding chapter of the Spring manual.
Spring Test Framework

Testing #TransactionalEvents and #Rollback

I've been trying to test out #TransactionalEvents (a feature of Spring 4.2 https://spring.io/blog/2015/02/11/better-application-events-in-spring-framework-4-2) with our existing Spring JUnit Tests (run via either #TransactionalTestExecutionListener or subclassing AbstractTransactionalUnit4SpringContextTests but, it seems like there's a forced choice -- either run the test without a #Rollback annotation, or the events don't fire. Has anyone come across a good way to test #TransactionalEvents while being able to #Rollback tests?
Stéphane Nicoll is correct: if the TransactionPhase for your #TransactionalEventListener is set to AFTER_COMMIT, then having a transactional test with automatic rollback semantics doesn't make any sense because the event will never get fired.
In other words, there is no way to have an event fired after a transaction is committed if that transaction is never committed.
So if you really want the event to be fired, you have to let the transaction be committed (e.g., by annotating your test method with #Commit). To clean up after the commit, you should be able to use #Sql in isolated mode to execute cleanup scripts after the transaction has committed. For example, something like the following (untested code) might work for you:
#Transactional
#Commit
#Sql(scripts = "/cleanup.sql", executionPhase = AFTER_TEST_METHOD,
config = #SqlConfig(transactionMode = TransactionMode.ISOLATED))
#Test
public void test() { /* ... */ }
Regards,
Sam (author of the Spring TestContext Framework)
Marco's solution works but adding REQUIRES_NEW propagation into business code is not always acceptable. This modifies business process behavior.
So we should assume that we can change only test part.
Solutin 1
#TestComponent // can be used with spring boot
public class TestApplicationService {
#Autowired
public MyApplicationService service;
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void doSomething() {
service.doSomething();
}
}
This wraps the real service into test component that can be decorated with REQUIRES_NEW propagation. This solution doesn't modify other logic than testing.
Solution 2
#Transactional
#Sql(scripts = "/cleanup.sql", executionPhase = AFTER_TEST_METHOD,
config = #SqlConfig(transactionMode = TransactionMode.ISOLATED))
#Test
public void test() {
MyApplicationService target = // ...
target.doSomething();
TestTransaction.flagForCommit(); //Spring-test since 4.1 - thx for Sam Brannen
TestTransaction.end();
// the event is now received by MyListener
// assertions on the side effects of MyListener
// ...
}
This is simplest solution. We can end test transaction and mark it for commit. This forces transactional events to be handled. If test changes data, cleanup sql script must be specified, otherwise we introduce side effects with committed modified data.
Sam Brannen's solution almost works with regard to adam's comment.
Actually the methods annotated with #TransactionalEventListener are called after the test method transaction is committed. This is so because the calling method, which raises the event, is executing within a logical transaction, not a physical one.
Instead, when the calling method is executed within a new physical transaction, then the methods annotated with #TransactionalEventListener are invoked at the right time, i.e., before the test method transaction is committed.
Also, we don't need #Commit on the test methods, since we actually don't care about these transactions. However, we do need the #Sql(...) statement as explained by Sam Brannen to undo the committed changes of the calling method.
See the small example below.
First the listener that is called when the transaction is committed (default behavior of #TransactionalEventListener):
#Component
public class MyListener {
#TransactionalEventListener
public void when(MyEvent event) {
...
}
}
Then the application service that publishes the event listened to by the above class. Notice that the transactions are configured to be new physical ones each time a method is invoked (see the Spring Framework doc for more details):
#Service
#Transactional(propagation = Propagation.REQUIRES_NEW)
public class MyApplicationService {
public void doSomething() {
// ...
// publishes an instance of MyEvent
// ...
}
}
Finally the test method as proposed by Sam Brannen but without the #Commitannotation which is not needed at this point:
#Transactional
#Sql(scripts = "/cleanup.sql", executionPhase = AFTER_TEST_METHOD,
config = #SqlConfig(transactionMode = TransactionMode.ISOLATED))
#Test
public void test() {
MyApplicationService target = // ...
target.doSomething();
// the event is now received by MyListener
// assertions on the side effects of MyListener
// ...
}
This way it works like a charm :-)

How to populate test data programmatically for integration tests in Spring?

I am looking for the recommended approach for populating test data programmatically in integration tests using spring / spring boot. I am using HSQLDB (inmemory).
There is the possibility to execute SQL scripts in spring for integration tests like this:
#Test
#Sql({"/test-schema.sql", "/test-user-data.sql"})
public void userTest {
// execute code that relies on the test schema and test data
}
Instead of writing SQL scripts I would like to insert data for multiple test methods in one integration test programmatically like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = BookstoreApp.class)
#IntegrationTest
public class BookRepositoryTest {
#Autowired
private BookRepository bookRepository;
#Before // not working
public void setUp() throws Exception {
bookRepository.save(new Book("Donald Duck 1", "Walt Disney", "0-14-020652-3"));
bookRepository.save(new Book("Donald Duck 2", "Walt Disney", "0-14-020652-4"));
bookRepository.save(new Book("Micky Mouse", "Walt Disney", "0-14-020652-5"));
}
#Test
public void findByTitle() {
List<Book> books = bookRepository.findByTitle("Duck");
Assert.assertEquals(2, books.size());
}
#Test
public void getByIsbn() {
Book book = bookRepository.getByIsbn("0-14-020652-4");
Assert.assertEquals("0-14-020652-4", book.getIsbn());
Assert.assertEquals("Donald Duck 2", book.getTitle());
}
}
Each Test of this example runs just fine when being executed separately. But the second one (getByIsbn) will fail, when running them together. So obviously #Before is the wrong annotation to use here, since the books will be inserted twice.
How can I enforce the database setup being executed only once?
Replacing #IntegrationTest with #Transactional (at the class level) should likely solve your problem.
Reasoning:
#IntegrationTest launches your entire Spring Boot application, but this appears to be overkill for your scenario.
#Transactional will cause your tests to execute within a test-managed transaction that will be rolled back after the test completes; code executed within the #Before method will be executed inside the test-managed transaction.

How to prevent test from cleaning up db before each test in TestNG?

I have a test like this:
#RunWith(SpringJUnit4ClassRunner.class),
#ContextConfiguration(locations = { "file:war/WEB-INF/application-context.xml" })
#Transactional
public class ServiceImplTest extends AbstractTestNGSpringContextTests
{
#Autowired
private Service service;
#Test
#Rollback(false)
public void testCreate()
{
.....
//save an entity to table_A
service.save(a);
}
}
It seems that the table_A will be cleaned up before each test running(not roolback after test ran),because after each test,all old data entries in the table are cleaned up,only new inserted entry by test is left.How to prevent this "cleaning" action?
The default behavior is to rollback the transactions in testing context. You can override this behavior using the #Rollback(false) annotation on a test method to not rollback the changes made to the DB during that particular test.
That said, it is recommended that each test case is independent and should have its own scenario setup, scenario execution and scenario tear down. Otherwise, the test failure behavior would be difficult to analyze if there are inter-dependencies among tests.

Resources