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 ;)
Related
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.
When I setup Spring with XML a can override component definitions in XML files that are loaded later.
It's very usefull for tests - I create default config set and than load it with addition test configuration that replaces some of components with specials (stubs, mocks, and so on).
Now i start to migrate to annotation based configurations and it causes some problems.
The direct way to use annotations is auto-discovering of packages with #Component
So I have
#Configuration
#ComponentScan({"some.pack1", "some.pack2"})
public class ProductConfig{}
And when
#Configuration
#Import({ProductConfig.class})
#ComponentScan({"test.pack"})
public class TestConfig{}
But it will cause conflict if I try to override components in test.pack
And what I can do?
After some investigations where are 3 answers with some issues on them
Worst - i can use #Filter on ComponentScan - it's worst way,
i must not import existed config (that can has some additional beans)
i must rescan all components, and explicitly define set of filters
i can use #Profile and activeProfiles - it's better, while it's more sophistical, implict, but
it means that i must to know at product classes that they can be disabled in some tests
not to use #ComponentScan on override Config and using #Bean insted of it
it's maybe well on test configurations, but it means that I lost ability to use #Component annotation
use setParent on contexts - it works well, but
it's explicit operation on implementation of ApplicationContext not on interface
it's not hard to setup if overriding services has #Autwire dependency on some components from overriden config - require manual register and refresh
What is best and standard way to override conigurations??? When I used XML-based it was not a problem...
#profile plays a crucial role while implementing the testing strategy for your service/code.
For example, in development, you may have:
public interface DataSource{
public String getHost();
}
Default implementation is
#Component
#Profile("Prod")
public class DevDataSource implements DataSource {
public String getHost(){
// return actual value
}
And the implementation for component tests(Fake impl)
#Component
#Profile("test")
public class StubbyDataSource implements DataSource {
public String getHost(){
return "some-host"; // return mocked data
}
Now you can write a test here which can act as integration test, unit test and component tests (https://martinfowler.com/bliki/ComponentTest.html)
In that way, your testing strategy would be much more elegant, concise and easy to maintain. Just by changing the profile, the same test can point to different environments (real or fake).
I am pretty new to Spring and I study using "Spring in Action" (fourth edition) by Craig Walls. The interest is not only on how to write code that is working, but also on the correct principles of using Spring.
Regarding the following piece of code from page 142, Listing 5.6:
public class HomeControllerTest {
#Test
public void testHomePage() throws Exception {
HomeController controller = new HomeController();
MockMvc mockMvc = standaloneSetup(controller).build();
mockMvc.perform(get("/")).andExpect(view().name("home"));
}
}
My questions are generated by the following thoughts:
The general understanding is that Spring uses Dependency Injection as a way to reduce the management overhead of object dependencies, increase modularity, simplify testing and code reuse. However, doesn't it imply that beans must be created and managed by the container? Since I started reading on the subject, the first detail that I memorized stated that new should never appear in a well-written piece of code that follows DI.
Could this be a solution in case we want to test a Stateful bean? I mean, if there are multiple independent tests to be run on the same instance, each of them testing the same state of the bean. Even though I found out that there is a suitable annotation for doing this (#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)).
Is there another use case that is difficult or impossible to solve otherwise (except for using new)?
A more 'to the letter' implementation would use #ContextConfiguration to specify the ApplicationContext.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = WebConfig.class)
#WebAppConfiguration
public class HomeControllerTest {
#Autowired
HomeController controller;
#Test
public void testHomePage() throws Exception {
MockMvc mockMvc = standaloneSetup(controller).build();
mockMvc.perform(get("/")).andExpect(view().name("home"));
}
}
yes, you shouldn't use new to create Spring bean instances (otherwise they're not Spring beans anymore) in production code. But the whole point of DI is to let you create and manually inject your objects with fake dependencies in unit tests. So the test code here is perfectly fine.
Yes, each unit test is free to create its own bean instance and to populate it the way it needs to. Stateful beans are extremely rare, though. They're usually stateless.
Another place where using new to create a Spring bean is precisely in #Bean-annotated methods of configuration classes. The whole point of these methods is precisely to create and initialize the Spring beans that will then be used and injected by Spring. But again, using new in unit tests is absolutely fine, and the right thing to do.
We have spring services in our project. for example UserService.
These services use the #Transactional annotation.
We just tried to execute junit tests that involves these services and for some reason the #Transactional annotations are ignored. (For example, i use a #Test without #Transactional , it goes to a service that is indeed anotated #Transactional, in that service it saves a new object(entity) the entity is being saved in the DB before the end of the transaction (Commit).)
I would like to point out that we don't want our junit tests to be annotated with #Transactional themselves.
Is it possible to enforce junit to not ignore the #Transactional annotations in our services?
Thanks,
You should explicitly tell JUnit to use transactions (example from reference):
#ContextConfiguration(locations={"example/test-context.xml"})
#TransactionConfiguration(transactionManager="txMgr", defaultRollback=false)
public class CustomConfiguredTransactionalTests {
// class body...
}
Then you can use #NonTransactional/#Rollback/etc annotations in order to control the behavior for particular methods.
Manual configuration is required as previously mentioned, as transactions are not persisted in Spring test contexts.
Spring reference for this topic can be found here
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.