I am trying to create tests for my app which connects to a database. The DataSource is a conection pool (Hikari).
Here is my test configuration:
#Configuration
public class SqlTestConfig {
#Bean
DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setMaximumPoolSize(2);
config.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
config.setJdbcUrl("jdbc:sqlserver://serversql:1433;database=myDatabase");
config.setUsername("user");
config.setPassword("password");
return new HikariDataSource(config);
}
}
Here is my test class:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = SqlTestConfig.class)
#Slf4j
#Sql(
scripts = "/clearTables.sql",
config = #SqlConfig(separator = "GO")
)
public class SqlTest {
#Autowired
DataSource dataSource;
#Test
public void test1() throws SQLException {
log.info("catalog:" + dataSource.getConnection().getCatalog());
}
#Test
public void test2() throws SQLException {
log.info("catalog:" + dataSource.getConnection().getCatalog());
}
#Test
public void test3() throws SQLException {
log.info("catalog:" + dataSource.getConnection().getCatalog());
}
#Test
public void test4() throws SQLException {
log.info("catalog:" + dataSource.getConnection().getCatalog());
}
}
Notice that the MaximumPoolSize is set to 2. When I run the test class the first two tests are successfully completed and the remaining tests fail because the pool gets depleted of connections (connection timeout).
I believe the problem is because of the #Sql annotation which causes DataSourceInitializer -s to be created to execute the cleanup script but the connections are never returned to the pool.
When I set MaximumPoolSize to 4 all tests are successfully completed. I cannot tell if I have made a configuration error or if this is a bug in Spring.
The getConnection acquires connection from underlying pool. Change your tests to properly close the acquired connection like so:
#Test
public void test1() throws SQLException {
try (Connection connection = dataSource.getConnection()) {
log.info("catalog:" + connection.getCatalog());
}
}
Related
I am working with Spring Boot and Dart. When I hit the URL (using POSTMEN/Browser) to insert some data in MySql I got the response correctly. But WHen I send the 3 requests consecutively from Flutter Front-end using Dart language it most of the time returned the result of 2 GET request and through the error for the 3rd request and most of the time it works for all request.
Following is the connection service that I am using on backend to store the data.
ConnectionService.java
#Service
public class ConnectionService {
private static final Logger log = LoggerFactory.getLogger(ConnectionService.class);
Connection connection = null;
#Value("${spring.datasource.url}")
String datasourceUrl = "jdbc:mysql://localhost/databaseName?autoReconnect=true&useSSL=false";
public Connection createConnection() throws SQLException {
connection = DriverManager.getConnection(datasourceUrl, "root", "root");
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
return connection;
}
public void closeConnection() {
try {
connection.close();
} catch (Exception e) {
log.error(e.toString());
}
}
}
I am creating the object of the Connection service class and call the createConnection() to create the connection and closeConnection() to close that one.
Controller.java
#RestController
#RequestMapping("/user")
public class UserController {
#Autowired
ConnectionService connectionService;
#GetMapping(path = "/test")
public void testFunction(#RequestParam(name = "abc") String abc) throws SQLException
{
Connection connection = connectionService.createConnection();
if (abc.isExist(param1,param2,connection))
{
//some code
connectionService.closeConnection();
} else
{
//some operation
connectionService.closeConnection();
}
}
Guide me to resolve this issue.
Thanks
They problem is that instead of closing the instance connection that I have created locally I am closing the main connectionService instance. This is the problem.
Now connectionService.closeConnection();
It should be connection.closeConnection();
I have database configuration in the properties file:
port=8080
host=host-default
host-default is obviously DNS. Below is my configuration class:
#Configuration
#Slf4j
public class DatabaseConfig {
#Value("${port}")
private int port;
#Value("${host}")
private String hostname;
#Bean
public DatabaseTemplate databaseTemplate() {
try {
return new DatabaseTemplate(client());
} catch (Exception e) {
log.error("Ex: " + e.getMessage(), e);
return null;
}
}
#Bean
public Client client() throws UnknownHostException {
TransportAddress address = new InetSocketTransportAddress(InetAddress.getByName(this.hostname), this.port);
client.addTransportAddress(address);
return client;
}
}
So, there is a problem. When the server is running, and in meantime I change DNS the connection with DB will fall dawn. At this moment I cant refresh configuration. I can catch moment when DNS change but I cannot update config. Have you any idea? I tried to destroy DatabaseTemplate singleton but It does not help. Thanks
You will need to create a new bean that wraps the database connection, then update it based on a schedule :
#Component
public class DataSourceManager implements DataSource{
private DataSource dataSource;
#PostConstruct
#Scheduled(fixedRate=1000)
public void reload() {
// init the datasource
}
public DataSource getDataSource(String dbName) {
return dataSource;
}
#Override
public Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
.... wrap all the other DataSource methods
}
I'm trying to create a database connection library to be used in all my apps. I want to make sure that this library is fully unit tested and so i'm trying to use dependency injection.
I have this class which i want to ensure is tested:
public class ConnectionFactory {
private String dataSourceName;
public ConnectionFactory(String dataSourceName) {
if(dataSourceName == null) {
throw new NullPointerException("dataSourceName can't be null");
}
this.dataSourceName = dataSourceName;
}
public Connection getConnection() throws SQLException {
Connection connection = getDataSource(dataSourceName).getConnection();
if(connection != null) {
return connection;
}
...
}
// Get a datasource object
private DataSource getDataSource(String dataSourceName) {
...
try {
Context ctx = new InitialContext();
dataSource = (DataSource) ctx.lookup("java:comp/env/" + dataSourceName);
} catch (NamingException e) {
...
}
return dataSource;
}
}
I want to be able to simply call this class from all my apps with something as simple as this:
public class MyApp {
public static void main(string[] args) {
ConnectionFactory connectionFactory = new ConnectionFactory("jdbc/myDataSource");
Connection connection = connectionFactory.getConnection();
}
}
I've started writing unit tests for this ConnectionFactory, but quickly realized that with my current code I can't mock the DataSource object so it's trying to actually connect to a real data source.
#RunWith(Nested.class)
public class ConnectionFactoryTest {
public class Constructor {
#Test
public void shouldThrowNullPointerIfParamIsNull() {
assertThatExceptionOfType(NullPointerException.class)
.isThrownBy(() -> { new ConnectionFactory(null); })
.withMessage("dataSourceName can't be null");
}
}
public class GetConnection {
public class WithDataSourceAvailable {
#Test
public void shouldErrorIfParamIsNull() {
ConnectionFactory connectionFactory = new ConnectionFactory("jdbc/myDataSource"); // <-- This is going to fail b/c it's trying to actually fetch a real data source
}
}
}
}
How can I properly use Dependency Injection so that I can write unit tests that don't actually try to connect to a data source?
Take a look at Mockito I find it easy to use for this type of mocking.
I have a class that needs to be unit tested:
public class AMQProducer {
private final String TCP = "tcp://";
private final String COLON = ":";
AMQProducer() {
}
public AMQProducer(String ip, long port) throws JMSException {
try {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(TCP + ip + COLON + port);
Connection connection = connectionFactory.createConnection();
} catch (JMSException e) {
throw e;
}
}
}
I have a test case:
#RunWith(PowerMockRunner.class)
#PrepareForTest()
public class TestAMQProducer {
#Mock
Connection connection;
#Mock
ActiveMQConnectionFactory connectionFactory;
#Test
public void test() throws Exception {
PowerMockito.whenNew(ActiveMQConnectionFactory.class).withAnyArguments().thenReturn(connectionFactory);
PowerMockito.when(connectionFactory.createConnection()).thenReturn(connection);
AMQProducer producer = new AMQProducer("random", 1234);
}
}
When I run the test case, the mock object of ActiveMQConnectionFactory isn't being used. Instead, the actual implementation is being used and there's a TCP connection being made:
javax.jms.JMSException: Could not connect to broker URL: tcp://random:2333. Reason: java.net.UnknownHostException: random
I tried with Powermockito and Mockito, but failed with both. How do I mock the objects and how do I successfully run the test case?
I am new to unit testing and tried to get help from various communities, but didn't find the appropriate answer. Any help would be appreciated. Thanks!
You cannot test it with the current structure of your code - you create new ActiveMQConnectionFactory in your constructor. Use dependency injection instead.
With Mockito:
public class AMQProducer {
AMQProducer() {}
public AMQProducer(ActiveMQConnectionFactory connectionFactory) throws JMSException {
Connection connection = connectionFactory.createConnection();
}
}
public class TestAMQProducer {
private final Connection connection = mock(Connection.class);
private final ActiveMQConnectionFactory connectionFactory = mock(ActiveMQConnectionFactory.class);
#Test
public void test() throws Exception {
doReturn(connection).when(connectionFactory).createConnection();
// actual test here
}
}
I want to test pushing and pulling data from a database. Will this pattern work?
Specifically, will using this pattern successfully push and pull from the database and will it clean up afterwards?
private Connection connection;
private Savepoint savepoint;
#Before
public void setUp() throws Exception {
connection = MyConnectionGenerator();
connection.setAutoCommit(false);
savepoint = connection.setSavepoint();
}
#Test
public void myTest() throws Exception{
//use the connection for CRUD ops testing
}
#After
public void tearDown() throws Exception {
connection.rollback(savepoint);
connection.close();
}