How can I bootstrap my Spring Boot 2 integration tests so that across all of them I can have one set of configurations which pre-seeds the test database with some test data that can be used across all integration tests?
Supposing you are using h2 test database.
My src/test/resources/application.properties file has:
spring.jpa.hibernate.ddl-auto=create-drop
You'll need a configuration file with the following structure. (This is a configuration example located inside the folder src/test/java):
#Profile("test")
#Configuration
public class H2Config {
#Autowired
private DataSource datasource;
#PostConstruct
public void loadSQL() throws Exception {
ScriptUtils.executeSqlScript(datasource.getConnection(), new ClassPathResource("/sql/load_database.sql"));
}
}
The file 'load_database.sql': (the full path is /src/test/resources/sql/load_database.sql)
CREATE OR REPLACE TABLE OPER_DISPN(
ID NUMBER NOT NULL,
DT_VCTO_OPER DATE NOT NULL
);
INSERT INTO OPER_DISPN(ID,DT_VCTO_OPER) VALUES (1,TO_DATE('2018/09/21', 'yyyy/mm/dd'));
If you are using mapped entities(with #Entity) ( with create-drop) you won't need the 'CREATE TABLE' part for that.
And now, all your integration tests have the script data inserted
Edit: ( My test structure) I've created at github my example application. Please see the test structure and run the tests:
TNotMappedRepository.testLoadDataFind()
PersonRepository.testLoadDataFind()
Github: https://github.com/thiagochagas/insert-data-tests
Related
The problem rears its ugly head when the JdbcJobInstanceDao attempts to call the FIND_JOBS_WITH_KEY query:
SELECT JOB_INSTANCE_ID, JOB_NAME from %PREFIX%JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?
the %PREFIX% token is replaced with the value of application.properties key spring.batch.table-prefix which defaults to "BATCH_".
The application properties are definitely loading from the files as my small test demonstrates:
#ActiveProfiles("test") // to load `application-test.properties`
#RunWith(SpringRunner.class)
// we don't need a web context as we are playing with only server side classes
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = {TestDatabaseConfig.class,
MyBatchProperties.class, SpringBatchTestConfig.class})
#ComponentScan(basePackageClasses = {MyBatchConfig.class})
// MyBatchConfig has #EnableBatchProcessing and all job configurations.
public class BatchTest {
#Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
#Autowired
private ApplicationContext applicationContext;
#Before
public void setup() {
Environment environment = applicationContext.getEnvironment();
System.out.println(environment.getProperty("spring.batch.table-prefix"));
// above prints MY_SCEHMA_USER.BATCH_ as expected
}
#Test
public void checkJobRuns() {
try {
jobLauncherTestUtils.launchJob();
} catch (Exception e) {
e.printStackTrace(); // <-- fails here with the query returning "table not found" because the prefix was not configured correctly.
}
}
}
application-test.properties:
spring.batch.table-prefix=MY_SCHEMA_USER.BATCH_
I've been working with custom configuration for job runs for a long time but the JobLauncherTestUtils doesn't seem to honour these configuration properties.
I need the different table prefix as the batch database tables are owned by a different schema to the connected database user. (i.e. MY_APP_USER trying to access MY_SCHEMA_USER.BATCH_JOB_INSTANCE). unqualified references to tables try (and fail) to resolve the batch tables against MY_APP_USER instead of MY_SCHEMA_USER.
I've tried to create a JobRepositoryFactoryBean bean and annotate it with #ConfigurationProperties("spring.batch"). However - along with this not working anyway - I don't see why I should configure these this way rather than with properties.
How to I get the Batch related beans properly configured with application properties in junit tests using JobLauncherTestUtils?
Fixed
The problem was because i was creating my own BatchConfigurer bean inside of the MyBatchConfig class.
The BatchConfigurer registers the properties for configuration with each component of the Batch framework (jobRepository, daos, etc...)
This meant that the properties were not being populated through the #Component annotated DefaultBatchConfigurer class. As to why I decided to place this piece of code in here even I - the author - Am not sure. I guess i need to not code after minimal sleep.
Thanks for indulging my stupidity!
I use JPA/Hibernate for my Spring boot app. I'm writing ftests for the REST controllers.
I want to alter the db state (insert some rows, etc) and test the REST controller to see if it returns/works as expected.
It doesn't sound right to use the Service layer to initialize the database (at the end that's what we want to test as it's an end-to-end test!).
What is the best way to initialize database in a test for Spring boot app?
Note
I've heard of DBUnit but it uses xml, and I'd rather use java code.
I know of three options here
1. import.sql in Hibernate
It's as simple as adding an import.sql to your src/test/resources folder. Inside import.sql you can type in your sql to insert data you want. You would also have to set hibernate-ddl-auto to create in application.properties.
eg. Application.properties
spring.jpa.hibernate.ddl-auto=create
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.datasource.url=jdbc:postgresql://localhost/asgard
spring.datasource.username=thor
spring.datasource.password=mjolnir
eg import.sql
insert into
account (id, createddate, modifieddate, password , username, disabled)
values (-1, now(),null, 'password','admin',false);
2. Spring Test DB Unit
Spring Test DB Unit is DB Unit specifically for Spring. This basically lets you add data and remove it after test on method as well as class level which is very useful. Check the link for more details. The xml actually makes it easier to manage the data you want to insert. You can also insert data programatically but it's more complicated and instructions should be there on the website.
#Test
#DatabaseSetup(value = "insert.xml")
#DatabaseSetup(connection="customerDataSource", value="insert-custs.xml")
public void testInsert() throws Exception {
// Inserts "insert.xml" into dataSource and "insert-custs.xml" into customerDataSource
// ...
}
eg insert.xml
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<Person id="0" title="Mr" firstName="Phillip" lastName="Webb"/>
</dataset>
Table name is person along with id,title,firstName and lastName as fields.
3. Using #Before annotation
Using #Before annotation in Test class to set up db.
GenericTest Class
#RunWith(SpringRunner.class)
#SpringBootTest
public class GenericTest {
public EmployeeRepository employeeRepository;
#Before
public void setUp() {
employeeRepository.save(createRandomEmployeeObject());
}
#Test
public void insertSuccess(){
List<Employee> employee = employeeRepository.findAll();
//Logic to get employee from rest API
//Assert if data from rest API matches one in db
}
}
By default, Spring Boot will create an embedded Elasticsearch. It can be turned off by setting spring.data.elasticsearch.cluster-nodes. However, I'm not sure how to do this in a JUnit test. For example, I have:
#Slf4j
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(RemoteElasticsearch.class)
#SpringBootApplication(
scanBasePackageClasses = {
}
)
#EnableElasticsearchRepositories(basePackages = "com.example.me.repo")
public class RemoteElasticsearch {
#Inject
private SomeRepo someRepo;
#Test
public void test(){
someRepo.save(new Something());
}
}
It connects to the remote elasticsearch if I set the appropriate environment variable (eg spring.data.elasticsearch.cluster-node=host:9300). Can I somehow set this value directly on this test?
Just create second application.properties file in src/test/resources with spring.data.elasticsearch.cluster-nodes disabled. Spring Boot will use this file instead PROD configuration from src/main/resources.
How can I set the name of an embedded database started in a Spring Boot app running in a test?
I'm starting two instances of the same Spring Boot app as part of a test, as they collaborate. They are both correctly starting an HSQL database, but defaulting to a database name of testdb despite being provided different values for spring.datasource.name.
How can I provide different database names, or some other means of isolating the two databases? What is going to be the 'lightest touch'? If I can avoid it, I'd rather control this with properties than adding beans to my test config - the test config classes shouldn't be cluttered up because of this one coarse-grained collaboration test.
Gah - setting spring.datasource.name changes the name of the datasource, but not the name of the database.
Setting spring.datasource.url=jdbc:hsql:mem:mydbname does exactly what I need it to. It's a bit crap that I have to hardcode the embedded database implementation, but Spring Boot is using an enum for default values, which would mean a bigger rewrite if it were to try getting the name from a property.
You can try it so:
spring.datasource1.name=testdb
spring.datasource2.name=otherdb
And then declare datasource in your ApplicationConfig like this
#Bean
#ConfigurationProperties(prefix="spring.datasource1")
public DataSource dataSource1() {
...
}
#Bean
#ConfigurationProperties(prefix="spring.datasource2")
public DataSource dataSource2() {
...
}
See official docs for more details: https://docs.spring.io/spring-boot/docs/current/reference/html/howto-data-access.html#howto-configure-a-datasource
I have datasource defined in my application.properties as Oracle database and that's ok, the controller & repository work fine, I can persist entities and fetch database records.
I have integration tests written. Before I connected my app with database I created some objects and persisted them in #PostConstruct method - and that was ok. But now, when I connected everything with database, my tests try to fetch records from the table which is pretty large.
I think that's due to my application.properties file in which defined datasource is Oracle database.
spring.datasource.driverClassName=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:#blabla
spring.datasource.username=blabla
spring.datasource.password=blabla
spring.jpa.hibernate.ddl-auto=none
spring.jpa.generate-ddl=false
I want to carry on tests using some in-memory HSQL, not real database. But I still want my controller and repository to use the real one. How can I do it? Is there any possibility to add second datasource in application.properties? Or maybe an easier solution?
In Spring 4 you have #Profile annotation so you can use its advantage.
Create another like application-test.properties file with it's own properties.
Create configuration for your tests:
#Configuration
#Profile("test")
public class TestConfiguration {
....
}
Then annotate your class with #ActiveProfiles annotation:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = TestConfiguration.class)
#ActiveProfiles("test")
public class TestRepository {
...
}
There are many ways to achieve this, one way is to use Spring profiles. The test profile would use a different properties file than the production one.
Spring has org.springframework.mock.jndi.SimpleNamingContextBuilder package that allows you to bind the dataSource programatically.
Sample Test class that I use:
public class MockJNDIContext {
#BeforeClass
public static void setUpClass() throws Exception {
SimpleNamingContextBuilder builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder();
OracleConnectionPoolDataSource ds = new OracleConnectionPoolDataSource();//Use your own Connection Pool DS here as you require
ds.setURL("jdbc:oracle:thin:#10.xxx.xxx.xx:9999:SID");
ds.setUser("USER");
ds.setPassword("PASSWORD");
//Bind it to the JNDI you need
builder.bind("java:comp/env/jdbc/MY/APP", ds);
}
#Test
public void test(){
System.out.println("JNDI Initialized");
}
}
Suite Class:
#RunWith(Suite.class)
#Suite.SuiteClasses({
MockJNDIContext.class,
InitializeTestConfig.class
})
public class ServiceSuite{
}
May be this helps? If you are trying to load from one more application.props, use one more class (InitializeTestConfig.class) that initializes and passes the DS args and add to suite as mentioned above and try?