How can I make Spring testcontext framework use multiple data sources? - java

I'm trying to integration test my application with Spring TestContext framework. I have done this by extending AbstractTransactionalJUnit4SpringContextTests, as usual. However, my application has three different data sources (with names like xDataSource, yDataSource, zdataSource), så when I try to run the test, the autowiring of data source in AbstractTransactionalJUnit4SpringContextTests won't work, since it looks for a Data Source with autowire-by-type, but finds three, so it does not know which one to choose.
Is there any way to get Spring TestContext Framework to use three data sources? If so; how?

OK, I figured it out. The answer to this question is twofold. Firstly, extending AbstractTransactionalJUnit4SpringContextTests won't work. This is because it needs a single data source for creating the SimpleJdbcTemplate for verifying stuff with simple JDBC queries in the test. Since I don't use this feature in this test, I could replace extends AbstractTransactionalJUnit4SpringContextTests with the collowing configuration:
#ContextConfiguration(locations = "classpath:applicationContext.xml")
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class
})
#Transactional
public class IntegrationTest {
...
}
The combination of these annotations gives the same setup as extending AbstractTransactionalJUnit4SpringContextTests.
The second part was understanding that since I have three data sources, I also need all three so be referenced by the same PlatformTransactionManager. I have distributed transactions. This is impossible with a DataSourceTransactionManager, so I had to use a JtaTransactionManager.

The AbstractTransactionalJUnit4SpringContextTests class is autowired to a single data source only to allow the convenience of providing an injected JdbcTemplate object. You can override the setDataSource(DataSource dataSource) method from AbstractTransactionalJUnit4SpringContextTests in your test subclass and specify the data source to use like this:
#Resource(name = "dataSource")
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
You just have to provide the name of the one data source Spring should use for the jdbcTemplate convenience methods. If extending AbstractTransactionalJUnit4SpringContextTests is more convenient than other methods mentioned above, then you can force it to work by just choosing one of your data sources.
I found these details in Spring Jira ticket #SPR-4634.

You can define one of the data-sources as primary="true" in your xml, and it will be chosen.
If you need all threem then you cannot rely on autowiring - use ReflectionTestUtils to set it manually in your tests.

Related

Autowired implementation based on profile Spring Boot

I am developing a REST API with Spring Boot.The problem it's that I have one interface and two implementations and I want to test only with the mock implementation.
Interface CRMService
#Service
CRMServiceImpl
#Service
CRMServiceMock
Implementations: the first one is the real integration with the backend and the second is a mock for testing purposes, what's the best approach? Integration test or test based on the active profile ? If I need to autowire a service based on profile what's the best practice?
While I'm sure there's exceptions, generally it shouldn't be integration or unit tests (often involves mocks), but both; see testing pyramid concept.
Integration tests: just use the real service. If it calls out to other live services, then consider injecting the URLs as Spring Boot properties which point to mock servers in the test environment (Node.js or something easy and quick).
Unit tests: Consider using a test-framework like Mockito. Using this you can write your tests with mocks approximately like so:
private CRMServiceImpl mockService = mock(CRMServiceImpl.class);
#Test
public void someTest() {
when(mockService.someMethod(any(String.class), eq(5))).thenReturn("Hello from mock object.")
}
The above example roughly translates to "when some class invokes 'someMethod(String, int)' on your service, return the String specified".
This way allows you to still use mocks where necessary, but avoids having to maintain entire mock implementation profiles and avoids the problem of what to auto-wire.
Finally, if you need a full separate implementation, consider not auto-wiring services! Instead, use #Bean annotations in your configuration class and inject it via constructors into the classes that need it. Something like so:
#Configuration
public class ApplicationConfiguration {
#Value{$"service.crm.inmem"} // Injected property
private boolean inMem;
#Bean
CRMService getCRMService() {
if (inMem) {
return new CRMServiceMock();
}
return new CRMServiceImpl();
}
#Bean
OtherService getOtherService() {
// Inject CRMService interface into constructor instead of auto-wiring in OtherService.class
return new OtherService(getCRMService());
}
}
An example of when you could use ^^ would be if you wanted to switch between an in-memory store, and a real database-connection layer.
Personally I'd suggest doing dependency injection like the above example even when there aren't multiple implementations since as a project grows, if an auto-wired property fails it can be difficult to track down exactly why. Additionally explicitly showing where dependencies come from can help with organizing your application and visualizing your application hierarchy.

Using a Class with a #Required Field in Unit Test with Mockito

I'm trying to unit test a class; for the sake of brevity we'll call it Apple. It has a #Required setter that takes an AppleManager bean. AppleManager itself has a #Required setter than takes an AppleStorageClient bean. I am mocking AppleManager and injecting it into Apple, but I also need to use the real AppleManager class to retrieve data using methods from its AppleStorageClient object. How can I achieve this with Spring/Mockito?
Test:
public class AppleTest {
#InjectMocks
private Apple apple;
#Mock
private AppleManager appleManager;
?????
private AppleManager realAppleManager;
//I tried = new AppleManager() here but the object is null...
//ostensibly because Spring doesn't know I want to use the bean
//also tried #Autowired to no avail
#Before
public void doBeforeStuff() {
MockitoAnnotations.initMocks(this);
}
...
}
Source:
public class Apple {
private AppleManager appleManager;
#Required
public void setAppleManager(AppleManager appleManager) {
this.appleManager = appleManager;
}
....
}
&
public class AppleManager {
private AppleStorageClient appleStorageClient;
#Required
public void setAppleStorageClient() {
this.appleStorageClient = appleStorageClient;
}
...
}
In general it looks like something is 'uncomplete' here. I'll explain why.
Technically If you're using spring - it doesn't sound like a unit test to me anymore, probably integration test or something.
Unit tests are in general should be really-really fast and starting up spring won't let them pass fast enough (think about having thousands of unit tests in your project each of them running spring on startup - it will take them ages to complete).
But let's say its only about definitions. When you're using spring testing framework with JUnit, someone has to start and maintain a spring context to do all the Dependency Injection magic and apply it to the test case.
In Junit implementation a special Runner (a JUnit abstraction) is required:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:my-test-context.xml" }) // or use Java Config
This doesn't appear in the question though.
So now Spring will create a context and will attempt to inject beans. And we have effectively reduced our issue to the issue of having two implementations of the interface and asking spring to inject implementation by interface so that two different implementations will be injected. There are 2 solutions I can see here:
Create a Mock outside spring - you probably won't specify your expectations in spring anyway. Maintain only a "real apple manager" in spring
Maintain both in spring but in your test case use a #Qualifier annotation
Now what I would like to emphasize is that if you maintain real apple manager that contacts "apple store" (probably a database, with driver support, transaction management and so forth) you'll have to create a test context so that it will be able to connect to that database, and if the apple manager internally injects its dependencies via spring, then these beans are also have to be specified.
So that if in future you'll change something in the underlying store (say, add a dependency in a driver to another spring bean, this test context will automatically become broken). Just be aware of this and inject beans wisely.

UnitTests and Spring - create new beans?

I have an application where I use Spring (annotations, not xml), and I need to load the beans in my unit tests. I have the AppConfig class from my code which I want to use, but with a different datasource (one I define in the test folder). This is because I want to use an in memory DB in my tests, and not the real DB.
Here's how I try to run the AppConfig class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {App.class, AppConfig.class})
public class DAOManagerTest {
//All code goes here
#AutoWired
UserDAO userDAO;
#Test
public void testGet() {
List<User> test = userDAO.selectAll();
for (User u: test) {
u.toString();
}
}
}
This doesn't entirely work, as it fails on creating a bean inside the UserDAO class. I guess I need some tutorial / guide on how to deal with spring in unit tests. Should I define new beans in my test folder, or is it possible to user the spring class from my code? Also, is it possible to define a seperate datasource for the tests?
Yes. For example, if you define some beans in DAOManagerTest, using #Primary if necessary, and add DAOManagerTest.class to #ContextConfiguration.
There are so many other ways of arranging it though, like using profiles or mocks, etc.

spring + testng + hibernate right approch for session factory

I am building a spring 4 + Hibernate5 application. I wonder whether is there any difference in defining the data base connection properties like url ,username etc via DataSource object and via hibernate properties like "hibernate.connection.url", "hibernate.connection.username" etc. Offcourse ultimately the datasource object will be tied to the session factory. Just want to make sure to do the things in right way.
I want to define a separate datesource object via dataSource property, so that I can use the AbstractTransactionalTestNGSpringContextTests for test cases. This classs is always expecting a data source object. I want to use the #Rollback feature and this feature is working with AbstractTransactionalTestNGSpringContextTests. AbstractTestNGSpringContextTests is not supporting the roll back feature but still persisting working perfectly.
Need inputs to implements in the right way.
Adding example code to provide more information.
#ContextConfiguration(locations = { "classpath:spring/fpda_persistence_config.xml" })
#Rollback
#Transactional
public class BankTransactionDAOTest extends AbstractTestNGSpringContextTests {
#Autowired
private BankTransactionDAO bankTransactionDao;
#Test
public void createBankTransactionTest(){
BankTransaction bt = new BankTransaction();
bt.setAuthoritativeTableId(new BigDecimal(1234));
bt.setBankTransactionTypeCode(new Character('C'));
bt.setInstanceId(new BigDecimal(1234));
bt.setRowCreatedEpochTime(new BigDecimal(1234));
bt.setTransactionId(new BigDecimal(1234));
bt.setTransactionKey(new BigDecimal(System.currentTimeMillis()));
bankTransactionDao.createBankTransaction(bt);
}
}
here to make transaction roll back happen sucessfully, I came to know that we should extend AbstractTransactionalTestNGSpringContextTests instead of AbstractTestNGSpringContextTests. Then I should have declared the datasource property instead of defining all properties in hibernate properties.
so overall is it a right approach to declare some properties in data source and some properties in hibernate?. will it make any difference.
Thanks in Advance.
In our project we are using class-level annotation for test classes
#TestPropertySource(locations = "classpath:application-test.properties")

JUnit multiple transaction managers for spring tests

I'm using Spring 3.0.5 and Junit 4.8.2
Is it possible to use multiple transaction managers during tests?
Basically I'm try for something like this. I need to add and remove content from two separate databases during the tests.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:/applicationContext-test.xml" })
#TransactionConfiguration(transactionManager = "txMgrA", defaultRollback = true)
#TransactionConfiguration(transactionManager = "txMgrB", defaultRollback = true)
#Transactional
public class SampleTest {
...
}
Looking at Spring 5 TransactionalTestExecutionListener implementation, it looks it only supports one TransactionManager per thread, what seems to be a design flaw of this listener, probably the same as you came across in 2011 :)
However, currently it's possible to workaround this using ChainedTransactionManager. If you have multiple transaction managers, you can define yet another transaction manager in your test context:
#Configuration
class TestTransactionConfig {
#Bean("testTransactionManager")
public PlatformTransactionManager chainedTransactionManager(
#Qualifier("transactionManager1") PlatformTransactionManager transactionManager1,
#Qualifier("transactionManager2") PlatformTransactionManager transactionManager2
) {
return new ChainedTransactionManager(transactionManager1, transactionManager2);
}
}
Now, you can define your base class for tests using this transaction manager:
#RunWith(SpringRunner::class)
#Transactional("testTransactionManager")
class BaseTransactionalTest {
}
For all derived classes all test methods will now be wrapped in both transactions, which will be finally rolled back by TransactionalTestExecutionListener.
Since Java will not allow multiple annotations of the same type per element, you must find another way to configure it. #TransactionConfiguration is interpreted by TransactionalTestExecutionListener, whose getTransactionManager method only returns a single PlatformTransactionManager. It looks at #Transactional but seems to ignore the value qualifier that was added in Seam 3.0.
#Transactional itself only supports a single transaction manager. How is the real application configured? You must be using #Transactional("<qualifier>") (as in the docs), right?
If you just use #Transactional with different tx managers on different methods, then the simplest solution is to just split your test class.
Are you nesting the transactions? That is, you have #Transactional("tm1") on one method, which calls a nested method that has #Transactional("tm2")? Sounds a little unusual. You could try to set up your test in the same way -- have two test #Services, each with the appropriate #Transactional annotations, that are proxied with tx:advice as usual. The outer service sets up the outer txn; the inner service sets up the inner txn and contains the actual test code. You can't use #Rollback, but hey, hacks ain't pretty.
Another option would be to create your own PlatformTransactionManager that delegates to the two other managers (for testing purposes only).
Maybe better would be to just give up and manually manage the two transactions in the test's #Before/#After methods.
Best would be to use JTA global transactions. Hopefully you're not actually nesting separate transactions and this is all moot ;)

Categories