I have a Rest controller GET method
#GetMapping("/users")
public List<Users> getAllUsers() {
return userRepo.findAll()
}
I need to test it, but I get the "Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured". The problem is that the nature of production does not allow me to use real connection or change it to embedded one. Is there a way to mock the DB somehow?
The project is almost the same to the one in this article:
https://dzone.com/articles/how-to-create-rest-api-with-spring-boot
Either you can use #MockBean annotation to mock the UserRepository or you can create an embedded in-memory H2 DB for the test by adding following properties in application-test.properties:
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
You can follow this link to understand it in detail.
Related
I am writing integration tests for a project with multiple datasources and want to test the repository and service layer. Problem is that the #Transactional annotation is not working and data inserted during tests is persisted.
This is a simple IT. I need to use #SpringBootTest instead of #DataJpaTest because multiple datasources are configured in a bean which handles assigning the right datasource to the correct repository.
#SpringBootTest(classes = { MyApp.class, TestSecurityConfiguration.class })
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#ActiveProfiles("database-test")
#Transactional
#Rollback
public class MyIT {
#Autowired
ChannelRepository channelRepository;
#Test
public void testChannels() {
Channel testChannel1 = new Channel();
testChannel1.setDescription("Test Channel 1");
channelRepository.save(testChannel1);
Channel testChannel2 = new Channel();
testChannel2.setDescription("Test Channel 2");
channelRepository.save(testChannel2);
Channel channel = channelRepository.findById(1).get();
assertThat(channel).isNotNull();
}
}
The dialect configured in the application.yml is "org.hibernate.dialect.SQLServer2012Dialect" and the database is our test database that runs on a dedicated server, not an embedded database.
The data is inserted without rollback.
After looking at the JpaTransactionManager logs using:
logging:
level:
sql: DEBUG
ROOT: DEBUG
org.springframework.orm.jpa: DEBUG
org.springframework.transaction: DEBUG
I saw that for each .save() call there was a new inner transaction. I tried to specify the transaction manager manually by passing the value to the #Transactional annotation and now it works. Looks like the DataSource beans are not picked up entirely correct. I got the idea based on this article where a ChainedTransactionManager is used for multiple datasources https://medium.com/preplaced/distributed-transaction-management-for-multiple-databases-with-springboot-jpa-and-hibernate-cde4e1b298e4
I'm developing an app in Spring Boot. I use a PostreSQL DB for production, and I want my JUnit 5 tests to run on a H2 memory DB. Problem is, after some configuration, the tests still don't seem to run on the in-memory db:
I can access entities from the prod db (a saved entity doesn't persist in the prod db tho)
if I grep through the test logs, I can see that Hibernate uses org.hibernate.dialect.PostgreSQLDialect
#DataJpaTest
#SpringBootTest
#TestPropertySource(locations = "classpath:application-test.properties")
#ExtendWith(SpringExtension.class)
#ActiveProfiles("test")
public class ClinicTest {
#Resource
private ClinicRepository clinicRepository;
#Test
public void givenClinic_whenSave_thenGetOk() {
var clinic = new Clinic();
clinic.setName("asd asd");
clinic.setShortName("asd");
// the ids of prod db entities are printed
clinicRepository.findAll().forEach(clinic1 -> System.out.println(clinic1.getId()));
// the id keeps incrementing between runs
System.out.println(clinicRepository.save(clinic).getId());
}
}
application-test.properties
jdbc.driverClassName=org.h2.Driver
jdbc.url=jdbc:h2:mem:testdb
hibernate.dialect=org.hibernate.dialect.H2Dialect
hibernate.hbm2ddl.auto=create-drop
spring.test.database.replace=none
What should I change to run self-contained test on a H2 in memory database?
Spring, by default, creates an H2 DB for the test. You don't need to specify it in your properties file.
Just add h2 in your pom.xml and set its scope to test. Spring Boot should handle the rest.
Your application-test.proprties does not have user and pasword
try adding this:
#--------------------- DB Connection ------------------
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=
#--------------------JPA-ORM Properties-----------------
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.properties.hibernate.format_sql=true
I need to make tests with embedded database and I want to check results in h2-console. I have configured properties for tests and I want to store my test data to have a look on it, but it always writes Replacing 'dataSource' DataSource bean with embedded version and uses another h2 DB like "jdbc:h2:mem:1f4af8a8-3e14-4755-bcbd-5a59aa31033e". What can I do with this problem?
"application-test.properties":
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:file:./subdirectory/demodb
spring.datasource.username=sa
spring.datasource.password=
spring.h2.console.enabled=true
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.format_sql=true
My test class:
#DataJpaTest
#ActiveProfiles("test")
class ProductRepositoryTest {
#Test
void findByProductName() {
//...
}
}
By default, the #DataJpaTest annotation replaces your production DB with an embedded one, wraps every test method in a transaction and rolls it back at the end of the test.
If you want to run your test as if it was a "real" operation on the DB, you can add the following annotations to your test class:
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#Transactional(propagation = Propagation.NOT_SUPPORTED)
The first one tells Spring not to use the embedded DB, while the second one tells it to work non-transactionally (every query will be persisted).
I am working with spring boot. I have properties defined in application.yml.
spring:
datasource:
username: username
password: password
username and password values are stored externally which program fetches during startup. let's say the bean which fetches them during startup is dbConfig
How can I inject values from dbConfgig to application.yml?
I am using spring-data-jpa autoconfigure which automatically connects to database at startup. I want these values to be loaded to application.yml before spring connects to database.
There is no need to inject the user/password in application.yml. You can set them programmatically like this:
#Configuration
public class DataSourceConfig {
#Bean
public DataSource getDataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName("org.h2.Driver");
dataSourceBuilder.url("jdbc:h2:mem:test");
// Take the values from external source, then set them
dataSourceBuilder.username("username");
dataSourceBuilder.password("password");
return dataSourceBuilder.build();
}
}
I think that first, you must create a thread to detect the change at your db Config file and then may you must re-init your bean (data source) to make your change effect.
See:
how-to-reinitialize-a-spring-bean
You may also try spring cloud to store properties. And you can then use with the help of placeholders.
https://cloud.spring.io/spring-cloud-config/reference/html/
Spring Boot : How to add new Datasource at runtime
My project want to connect two datasource.
The first datasource I can Config in application.properties but the second datasource can't config because this config is in the tableConfig from DB of the first datasource.
So,
config the 1st datasource.
query data from the 1st datasource for get config of 2nd datasource (url, username, password).
add new 2nd datasource
Now, I config two Datasource from application.properties and it's work.
But the requirement want to change the 2nd datasource from table of 1st datasource. T.T
Please, gives me some suggestions.
Thank you.
A Spring configuration like this should work (consider it pseudo code):
#Bean("secondDatasource")
public Datasource secondDatasource(#Qualifier("firstDatasource") Datasource ds){
// use `ds` to obtain the necessary information to obtain a datasource ...
return DataSourceBuilder
.create()
.username(username)
.password(pwd)
.url(url)
.driverClassName(driver)
.build();
}
I would at least start without using Spring Data JPA in the configuration class and operate directly on the data source to keep things simple.
You already got pointers, how to set up Spring Data JPA to then use the different data sources: http://www.baeldung.com/spring-data-jpa-multiple-databases
The code above is mainly just copied from: https://stackoverflow.com/a/28822145