Spring boot + Embedded Kafka + h2 database + unit Tests - java

So I am using this example Embedded Kafka and this too
I have changed this example little bit and updated the kafka listener with some database (Like h2 db).
Now in my unit test when I want to check that data is available in DB or not I am getting NULL. Also I am not sure how to check DB manually as h2 is a memory base DB.
here is the updated part:
in receiver class
#Autowired
DataTableRepository repository;
#KafkaListener(topics = "${kafkatest.topic}")
public void receive(ConsumerRecord<String, DataTable> consumerRecord) {
LOGGER.info("received payload='{}'", consumerRecord.toString());
repository.save(consumerRecord.value());
latch.countDown();
}
And in unit test :
#Autowired
DataTableRepository repository;
#Test
public void testReceive() throws Exception {
DataTable table = new DataTable(1, "Sending with default template");
template.send(topic, table);
receiver.getLatch().await(10000, TimeUnit.MILLISECONDS);
DataTable dt = repository.getOne(table.getId());
assertNotNull(dt);
assertThat(receiver.getLatch().getCount(), equalTo(0L));
}
But dt is always getting null. Also i am not able to check Database also, as it get stopped after test stopped.
Anybody has any idea how to make this workable?

Have you set the property "kafkatest.topic" in the test properties file? That might be the reason that your listener is not listening to the topic specified.

Related

#BeforeAll JUnit/spring-boot-test alternative that runs when application context starts

I'm writing a #Repository/#Service integration test that leverages an embedded database. In my test class, I would like to preload my database with some data.
I'm currently using #BeforeEach to load in my sample data, however, this code is run upon each test in my class.
Is there any way that I can load in my test data after Spring application context has loaded, but before any test has been run?
My current approach:
#BeforeEach
public void before() {
repository.save(...); // -> prepopulates repository with sample data
}
#Test
public void testService() {
service.get(...); // -> gathers existing record
}
#Test
public void deleteById() {
service.delete(...); // -> deletes existing record
}
However... with this, I am required to flush out the records after every test. Otherwise any unique constraints can easily be violated.
Rather than using #BeforeEach which is required to run before every test... is it possible to load this in in a #BeforeAll kind of fashion that happens after the spring application context has been loaded?
Is there any way that I can load in my test data after Spring application context has loaded
Basically yes, I think you can do that:
The idea is to load the SQL data when the application context is started or in the process of being started.
For example, spring boot integration with Flyway works this way (the bean of Flyway is created and loaded). So, in theory, you could merely use Flyway with test migrations that will contain all the relevant SQL scripts of test data generation.
How can you do this technically?
Here is one way:
Create a special bean (just like the way it works with Flyway) that would depend on your repository and in post construct save the data:
#Component
public class SqlGenerationBean {
#Autowired
private MyRepository repo;
#PostConstruct
public void init() {
repo.save();
}
}
Another way of doing is to create a listener that will be called upon the application context started and again will call the same repo.save().
In both cases the bean/listener code should not be accessible from production (it's only for tests): so put it somewhere under src/test/java for example
Now once the application context is started you can use a neat trick:
Mark your tests with #Transactional annotation. Spring will wrap the code in an artificial transaction that will be rolled back automatically (even if the test succeeds) so that all the data that you'll modify during the test will be rolled back and basically before each test, you'll have the same state (that is identical to the state of the database when/after the application context starts). Of course, if you use DDL in the test, some databases can't make it a part of transaction but it depends on the database really.
Another interesting point here is that the application context can be cached even between the test cases (created only once), so keep this in mind.
In this case I would just create a constructor for the test class. It will be triggered before everything.
#BeforeEach runs before each tests but after all initialisations .
you can also just use Mockito and mock the result without need to clean and overcomplicate
Just add following snippet to your code. This is just like you can do to detect that Spring application is really started.
#Configuration
public class AppConfig implements ApplicationListener<ApplicationReadyEvent> {
/**
* This is to indicate in the logs when the application has actually started and everything is loaded.
*/
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
ApplicationContext context = event.getApplicationContext();
Environment env = context.getEnvironment();
// do what you want on application start
}
}
P.S. For database manipulation in test #Sql is the best candidate as was mentioned in comment.

Junit Spring avoid to load twice application context datasource

I have this configuration classes:
#ComponentScan(
basePackages = {
"mypackage.controller",
"mypackage.service",
"mypackage.repository"
}
)
#TestPropertySource(locations="classpath:configuration.properties")
#Import({
H2Configuration.class
})
public class TestConfiguration {
}
#Configuration
public class H2Configuration {
#Bean
public DataSource dataSource() throws SQLException {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder
.setType(EmbeddedDatabaseType.H2)
.addScript("h2/create.sql")
.addScript("h2/insert.sql")
.build();
db.getConnection().setAutoCommit(false);
return db;
}
}
And I have this two class tests:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=AnnotationConfigContextLoader.class, classes = { TestConfiguration.class })
public class FirstRepositoryTest {
#Autowired
MyFirstRepositoryImpl repository;
#Before
public void initTest() {
}
#Test(expected = NullPointerException.class)
public void testNullRecords() {
repository.foo(null, null);
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=AnnotationConfigContextLoader.class, classes = { TestConfiguration.class })
public class SecondRepositoryTest {
#Autowired
MySecondRepositoryImpl repository;
#Before
public void initTest() {
}
#Test(expected = NullPointerException.class)
public void testSomethingNullRecords() {
repository.something(null, null);
}
}
If I run junit test once for each class, all goes well.
In clean install phase tests fails because the application context is initialized twice.
For example it try to create the h2 tables twice and do the insert.sql script twice.
What I have to do for initialize the h2 database and so application context only once?
Thanks
I think you could start looking at the Spring documentation about Integration Testing.
It can also be a good practice to use transactional tests for integration tests (#Transactional), which rollback at the end of each test : see Transaction Management.
To avoid the cost of recreating the ApplicationContext for each test class, the cache may be used as explained here : Context Caching.
For integration testing with Embedded Database, you can also find documentation : Testing Data Access Logic with an Embedded Database.
A note from the previous link, matching your use case :
However, if you wish to create an embedded database that is shared
within a test suite, consider using the Spring TestContext Framework
and configuring the embedded database as a bean in the Spring
ApplicationContext as described in Creating an Embedded Database by
Using Spring XML and Creating an Embedded Database Programmatically.
I hope you will find some useful references.
Another good tip I found from Spring Boot documentation from Embedded Database Support :
They say :
If you are using this feature in your tests, you may notice that the
same database is reused by your whole test suite regardless of the
number of application contexts that you use. If you want to make sure
that each context has a separate embedded database, you should set
spring.datasource.generate-unique-name to true.
So to make each EmbeddedDatabase unique, you may try to create them with :
EmbeddedDatabase db = new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
...
.build();
In unit testing you must garantee that every test is repeatible hance context independent. Due to this is not good idea to load the context only once. Is better to reset after the execution. For this you can use #DirtiesContext(classMode = ClassMode.AFTER_CLASS) in your test classes
So you will force your context to restart when the next junit class is launched
So the reason that this is failing is that the database (H2) is resident in memory when you run the tests as part of clean/install. The create/insert scripts have already executed after the first test is run. Any subsequent test execution after this point will result in a re-execution of the same script(s) and the error will occur.
Update your create script with a DROP TABLE IF EXISTS <table name>;. This will ensure that the table is dropped then recreated.
NOTE: I'm not sure why you've specified AnnotationConfigContextLoader explicitly. I think, without that, the runner SpringJUnit4ClassRunner will cache contexts that have not been changed. I don't know specifically if that is the case here though.

How to write an exception to allow application to run if database is not found?

I currently have a jdbcUrl oracle database binded to my cloud foundry application. I want my application to be able to build and run even if the service is not binded. Meaning it would throw a null exception, but still allow the app to run. (just wouldn't display any data).
SQLREPO
#Repository
public class myTableRepo {
#Autowired
JdbcTemplate jdbcTemplate;
#Transactional(readOnly=true)
public List<myTable_Model> getAll(String id) {
String sql = "SELECT * from myTable order by last_modified desc";
return jdbcTemplate.query(sql,
new myTableRowMapper());
}
}
RowMapper class
class myTableRowMapper implements RowMapper<myTable_Model>
{
#Override
public myTable_Model mapRow(ResultSet rs, int rowNum) throws SQLException {
myTable_Model model = new myTable_Model();
model.setId(rs.getString("id"));
model.setName(rs.getString("name"));
model.setlast_modified(rs.getString("last_modified"));
return model;
}
}
How would I write an exception if database is not found. Catch it and continue my application?
It looks like you're using Spring, so I would suggest that you take a look at profiles.
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-definition-profiles
With profiles, you can configure a different set of beans for different environments. For example, you could configure a "local" profile that runs and configures an in-memory database, and you can configure a "cloud" profile for when your app is running on Cloud Foundry. When you deploy your app to Cloud Foundry, the Java build pack will activate the "cloud" profile automatically (this can work without CF too, you just manually have to enable the profile). Instructions for enabling profiles are here.
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-definition-profiles-enable
Spring Boot makes enabling profiles even easier. It also allows you to configure conditional beans, for example, if bean A doesn't exist then configure X. This way you could have a default in-memory database that is configured if no other datasource is configured.
You could catch only exceptions related to SQL connection and then just log it.
#Transactional(readOnly=true)
public List<myTable_Model> getAll(String id) {
List<myTable_Model> result = new ArrayList<>();
try {
String sql = "SELECT * from myTable order by last_modified desc";
result = jdbcTemplate.query(sql, new Clone_HistoryRowMapper());
}
catch(SQLException ex) {
logger.log("An error happened when interacting to the database.", ex)
}
return result;
}
This way the app would continue if an error only related to SQL happens and stop id some other error occurs.

Hoverfly simulationMode conflicts with Spring Cloud Config Server in unit test

I'm using hoverfly in my spring boot project's unit test.
The background
The spring boot project will grab its config (connection timeout etc.) from spring cloud config server.
To test whether my timeout configs work, I write a unit test, and expect the hoverfly can return with a long delay , then my customized restTemplate can throw timeout error instead of wait.
The unit test looks lilke this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = TestApplication.class)
#FixMethodOrder(value = MethodSorters.NAME_ASCENDING)
public class CustomRestTemplateTest {
#Autowired
private RestTemplate customRestTemplate;
#ClassRule
public static HoverflyRule hoverflyRule = HoverflyRule.inSimulationMode(SimulationSource.dsl(
service("www.test.com")
.get("/")
.willReturn(success(HttpBodyConverter.json("{}")).withDelay(10, TimeUnit.SECONDS))
));
#Test
public void connectionTimeoutTest() {
customRestTemplate.getForObject("www.test.com", Object.class);
}
}
The issue
As I mentioned in section The background, when my spring boot project starts, it will grab configs from spring cloud config server, but Hoverfly captured that request and try to find the corresponding record, of course it can't , because I only defined the records for my unit test(e.g. www.test.com), so it throws error:
{"destination":"172.16.2.84:8888","error":"No match found","key":"a7ac72c9bcc3dc2b76bf0877d98f9e3a","level":"warning","method":"GET","msg":"Failed to find matching request template from template store","path":"************","query":"","time":"2017-03-08T20:55:28+08:00"}
How could I fix this? I want use hoverfly, can I set some config and exclude config server's url?
Hoverfly's developer Tommy responded me in their email list
It's a known issue: https://github.com/SpectoLabs/hoverfly-java/issues/19
Update
This has been fixed by Tommy Situ, and the code fix will be release in v0.4.3

Inserting seed data on application startup fails all my test cases

In my web application I have to insert some seed data if not exists in DB when application got deployed. So, I have triggered inserting data api as follows
#Component
public class ApplicationContextLoadListener implements ApplicationListener<ContextRefreshedEvent>{
#Autowired
private PersistenceFacilitator persistenceFacilitator;
#Autowired
private SeedDataService seedDataService;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// generate metadata for entities and schema
persistenceFacilitator.facilitate();
//insert seed data
seedDataService.insertSeedData();
}
}
It's working fine. But the problem comes when I execute test cases with #RunWith(SpringJUnit4ClassRunner.class), that event got triggered and try to insert seed data though DB is not ready. So, all my test cases getting failed.
Is there a way to address this issue with spring or any good application start-up event to handle seed data?
[updated]
As per app engine specification to test inserting data into DB, I have to call
#Before
public void setup(){
LocalServiceTestHelper helper = new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());
helper.setUp();
}
Which is not ready by the time application context loads. Anyway I don't want to insert seed data in DB while running testcases

Categories

Resources