Spring dependencies not being injected into BeforeSuite method? - java

I am running a spring boot application that uses TestNG as the testing framework. My tests are set up like this:
A parent class, which is in charge of setup logic and takes care of all of the configuration stuffs:
#ContextConfiguration(classes = {TestingConfig.class}, initializers = ConfigFileApplicationContextInitializer.class)
#ContextConfiguration(classes = TestConfig.class)
#TestPropertySource(locations = "classpath:application.yml")
public abstract ParentTestClass extends AbstractTestNGSpringContextTests {
#Autowired
private ServiceClient serviceClient;
#BeforeSuite
public void beforeClass() {
Assert.assertNotNull(serviceClient);
serviceClient.doSomeSetupWork();
}
}
There are multiple child test classes. Each on inherits form the parent test class so that they share the same setup logic.
public ChildTestClass1 extends ParentTestClass {
#Test
public void someTest() {
...
}
// More tests not shown
}
public ChildTestClass2 extends ParentTestClass {
#Test
public void anotherTest() {
...
}
// More tests not shown
}
The serviceClient is a client for one of the web services that the test suite depends on. I am making calls with the service client to set up the data in the other service before running the test cases.
The problem is this: previously I was using the #BeforeClass annotation, which meant that the parent class's setup method was being run once for every child test class. This was ok, but it was really slow waiting for the same setup to be run multiple times.
So I thought to myself: I'll just change the #BeforeClass annotations in the ParentTestClass to be #BeforeSuite instead! That will solve all of my problems!
Wrong.
Now when I run it, the Assert.assertNotNull(serviceClient); line in the beforeClass() method of the parent class fails. In short, Spring dependencies aren't being injected into the #BeforeSuite annotated method, even tho they were being injected in the method when it was annotated with #BeforeClass.
Any thoughts here? I'd really appreciate it!

I believe this is working as designed. From looking at how the implementation is built within org.springframework.test.context.testng.AbstractTestNGSpringContextTests (from which you extend), the dependencies are injected into your test class via the org.springframework.test.context.support.DependencyInjectionTestExecutionListener#injectDependencies (this is a listener).
All the listeners including the DependencyInjectionTestExecutionListener is invoked only via org.springframework.test.context.testng.AbstractTestNGSpringContextTests#springTestContextPrepareTestInstance which is a #BeforeClass(alwaysRun=true) classified method.
So your dependencies aren't available to you until and unless this #BeforeClass annotated method runs to completion. So you would have to move out your #BeforeSuite method and have it work with a #BeforeClass annotation only.
If you don't need your service setup to be done multiple times, then you would need to add an edit check in your test code that does the setup ONLY IF ITS NOT DONE.

Here is the solution to all your problem.
#Override
#BeforeSuite
protected void springTestContextPrepareTestInstance() throws Exception {
super.springTestContextPrepareTestInstance();
}
Hope this helps you to inject the dependencies.

Related

Spring issue with #PostConstruct and #PreDestroy

I have a Spring application that I am trying to test with EmbededRedis. So I created a component like below to Initialize and kill redis after test.
#Component
public class EmbededRedis {
#Value("${spring.redis.port}")
private int redisPort;
private RedisServer redisServer;
#PostConstruct
public void startRedis() throws IOException {
redisServer = new RedisServer(redisPort);
redisServer.start();
}
#PreDestroy
public void stopRedis() {
redisServer.stop();
}
}
But now I am facing a weird issue. Because spring caches the context, PreDestroy doesnt get called everytime after my test is executed, but for some reason, #PostConstruct gets called, and EmbededRedis tries to start the running redis server again and again, which is creatimg issues in the execution.
Is there a way to handle this situation by any mean?
Update
This is how I am primarily defining my tests.
#SpringBootTest(classes = {SpringApplication.class})
#ActiveProfiles("test")
public class RedisApplicationTest {
Ditch the class and write an #Configuration class which exposed RedisServer as a bean.
#Configuration
public void EmbeddedRedisConfiguration {
#Bean(initMethod="start", destroyMethod="stop")
public RedisServer embeddedRedisServer(#Value("${spring.redis.port}") int port) {
return new RedisServer(port);
}
}
So I debuged the ContextInitialization as suggested by #M. Deinum.
For me, the porblem was, Our application was mocking different classes in order to mix mocking with Spring context.
Now, when you use mocks, MockitoContextInitializer also becomes part of your cache key, which results in cache miss. Reason is, The classes under mock are obviously different for different test classes.
Looking at the situation, I preferred to go ahead with #DirtiesContext to invalidate the contest after the test is done, so that I can reinitialize the context later on for different test.
Note #DirtiesContext is in a way recommended to be avoided as it slows down your tests.

Spring Boot Test #PreAuthorize in a Library Module

I have a library-module written using Spring Boot 1.5.21.
The library has a #Service with some methods annotated with #PreAutorize allowing only users with ADMIN authority to perform some actions, for example a delete.
Then I have a Spring Boot Application that uses that library. If I run the app and manually test it, it works. Only ADMINs can delete. I'd like to write test for it.
I was thinking in writing separate test for module and for the main app. In my module I can successfully unit test the operations. But I'm having troubles testing the #PreAuthotize, because the security context does not exist in the module, but in the main app. Is there a way to test that annotation in the module, and not in the main app?
The relevant code of my library:
#Service
public class MyService {
#PreAuthorize("hasAuthority('ADMIN')")
public void delete (long id){
.....
}
}
#RunWith(SpringRunner.class)
public class MyServiceTest {
private MyService service;
#Test
#WithAnonymousUser
public void deleteShouldFailIfAnonymous() {
... Verify exception thrown
}
#Test
#WithMockUser(authorities = 'USER')
public void deleteShouldFailIfNotAdmin() {
... Verify exception thrown
}
#Test
#WithMockUser(authorities = 'ADMIN')
public void deleteShouldPass() {
... Should pass
}
}
I've trying adding #EnableGlobalMethodSecurity(prePostEnabled = true) but with no luck. And as said, the SecurityConfiguration is loaded in the main app, it does not exist in the library-module.
Can I test the #PreAuthorize in the module or should I move it to the main app? I'd like to have it i the module if possible. Or maybe I can create a Context only for testing, but don't know how to do it.
Aspect-oriented features like #PreAuthorize are implemented as advice. Usually, this means that Spring will create a decorator interface that intercepts the method call, performs the advice (in this case, checking the condition and throwing an exception if it fails), and then sends the call on to the service object.
When you use new MyService, you're creating a bare object without the wrappers that implement the advice, so you won't see security, validation, caching, etc., applied. To see the behavior you want, you need to make your test bean #Autowired using the Spring test support.
You can use the MockMvc to test. This will help with module testing incase of integration testing.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#WebAppConfiguration
public class SecurityTests {
#Autowired
private WebApplicationContext context;
private MockMvc mvc;
#Before
public void setup() {
mvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();
}
#WithMockUser(roles="ADMIN")
#Test
public void withMockUser() {
mvc.perform(get("......"))
}
}

Unit testing code inside TransactionSynchronizationManager

I couldn't find a working solution, how to unit test something that happened inside TransactionSynchronizationManager.registerSynchronization(). I have code like this:
pubclic class MyService {
public void method() {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter()
{
#Override
public void afterCommit()
{
myService.callSmth(params);
}
}
}
}
Here I call method():
public class MyClass {
private MyService myService;
public void method2() {
myService.method();
}
}
In my unit test I need to verify that myService.callSmth. But seems like test code doesn't go into afterCommit() method.
That's my test class:
#Configurable
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath*:test-applicationContext.xml" })
public class ServiceTest extends TestHelper
{
#Mock
MyService myService;
#InjectMocks
MyClass myClass;
#Test
public void allowWriteOffGlobal()
{
myClass.method();
veryfy(myService).method();
}
}
I simplified my code and please, don't pay attention if there could be any mistakes in mocking and smth like this. In a debugger I see how the flow comes to line TransactionSynchronizationManager.registerSynchronization and skips the afterCommit() method.
P.s.: not sure I provided enough info. Please, write in the comment if there is a need of smth extra. Thanks.
From Spring Test documentation (emphasis mine):
Annotating a test method with #Transactional causes the test to be run within a transaction that will, by default, be automatically rolled back after completion of the test. If a test class is annotated with #Transactional, each test method within that class hierarchy will be run within a transaction. Test methods that are not annotated with #Transactional (at the class or method level) will not be run within a transaction. Furthermore, tests that are annotated with #Transactional but have the propagation type set to NOT_SUPPORTED will not be run within a transaction.
and
By default, test transactions will be automatically rolled back after completion of the test; however, transactional commit and rollback behavior can be configured declaratively via the #Commit and #Rollback annotations at the class level and at the method level.
So I suggest putting a #Commit annotation in your test method. (By the behaviour you describe I suppose that you are using Spring Test, this advice does not apply otherwise).
Try mocking TransactionSynchronizationAdapter and call afterCommit()

Junit method invocation fails due to spring injection

I have written Junit test class to test particular method. One of the variables being processed in this method is spring injected, by taking the value from properties file.
Below is my test method
#Test
public void myTestMethod() {
//invoking the method to be tested
Assert.assertTrue(updateGroceries());
}
This is the class to be tested,
public class ToBeTested {
//Spring injected value
String categories;
public boolean updateGroceries() {
List<String> categoryList = StringUtils.convertStringToList(categories);
}
In the above class, categories variable is spring injected.
This is properties file content:
categories = Dals,Pulses,Dry Fruits,Edible Oil
Now while running my Junit method, execution is failing because dependency injection is failing.Since the code I want to test runs on tomcat. I want to test the code without running tomcat. Please suggest some solution.
First of all to run mockito you need to enable it over your test.
Using annotation #RunWith(MockitoJunitRunner.class) or execute at the beginning of your test Mockito.initMocks().
Then your test should look like:
#RunWith(MockitoJunitRunner.class)
private YourTest{
#InjectMocks
ToBeTested toBeTested;
#Mock
ToBeTestedDependency dependency;
#Before
public void setUp(){
ReflectionTestUtils.setField(toBeTested, "categories",
"someCategory");
}
#Test
public void shouldDoThisOrThat(){
toBeTested.updateCategories();
}
}
Unfortunately mockito doesn't support injecting #Valueannotated field. You need to use ReflectionTestUtils or setup run your test with SpringJUnit4ClassRunner where you need to define your spring context with PropertyPlaceholder configuration to resolve property that you have as your Value key. There you can find reference to documentation and example of spring testing approach.
Hope this helped.
You should look at Mockito. When you use mockito framework, you can create mocks for spring injected values. You should read more on mockito website.

Arquillian + TestNG: How to access container managed objects in #Before/#After methods?

I couldn't find any satisfying solution for this problem, though other people have encountered it before...
I'd like to test a business bean which modifies persistent data using a dao.
The dao can be injected into the test methods as it is an ejb.
How to make it available in typical #Before/#After methods, for example to clean up the db.
Brief Example:
#PersistenceTest
public class MyTestClass extends Arquillian {
#Inject private Dao dao;
#Inject private MyBean myBean;
#BeforeMethod
public void cleanDB () {
dao.remove(foo); // Currently throws NPE as dao is not injected.
}
#Test
public void someTest () {
// In a Test-method dao is available and calling cleanDB from here also
// works as intended....
}
}
As far as I know only the Test-methods are executed in the container. Most information that I found seems to be outdated.
Is there any nice way to achieve this?
Thank you!
I'm using (managed) Wildfly 8 as app server.
Arquillan invokes the #Before** and #After** methods twice.
Once in client mode, once in container mode.
The only solution I found so far is, that you must verify that the dao was injected before you use it like :
#BeforeMethod
public void cleanDB () {
if (dao != null) {
dao.remove(foo);
}
}
So if you make a breakpoint and runs your code you should have 2 invokations of this method:
1st: dao is null
2nd: dao is injected
Hope that helps.
See also http://jayshaughnessy.blogspot.de/2012/11/arquillian-and-testng.html for more information.

Categories

Resources