How to access Spring context in jUnit tests annotated with #RunWith and #ContextConfiguration? - spring

I have following test class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"/services-test-config.xml"})
public class MySericeTest {
#Autowired
MyService service;
...
}
Is it possible to access services-test-config.xml programmatically in one of such methods? Like:
ApplicationContext ctx = somehowGetContext();

This works fine too:
#Autowired
ApplicationContext context;

Since the tests will be instantiated like a Spring bean too, you just need to implement the ApplicationContextAware interface:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"/services-test-config.xml"})
public class MySericeTest implements ApplicationContextAware
{
#Autowired
MyService service;
...
#Override
public void setApplicationContext(ApplicationContext context)
throws BeansException
{
// Do something with the context here
}
}
For non xml needs, you can also do this:
#RunWith(SpringJUnit4ClassRunner.class)
/* must provide some "root" for the app-context, use unit-test file name to the context is empty */
#ContextConfiguration(classes = MyUnitTestClass.class)
public class MyUnitTestClass implements ApplicationContextAware {

If your test class extends the Spring JUnit classes
(e.g., AbstractTransactionalJUnit4SpringContextTests or any other class that extends AbstractSpringContextTests), you can access the app context by calling the getContext() method.
Check out the javadocs for the package org.springframework.test.

It's possible to inject instance of ApplicationContext class by using SpringClassRule
and SpringMethodRule rules. It might be very handy if you would like to use
another non-Spring runners. Here's an example:
#ContextConfiguration(classes = BeanConfiguration.class)
public static class SpringRuleUsage {
#ClassRule
public static final SpringClassRule springClassRule = new SpringClassRule();
#Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
#Autowired
private ApplicationContext context;
#Test
public void shouldInjectContext() {
}
}

Related

How to inject JpaRepository to not service class

I have interface like this
#Repository
public interface CarRepository extends JpaRepository<Car, Long> {
}
In class adnotated #RestController Spring is able to wired with proper field.
But how can I do this if I want to create class without any adnotation in spring? I have to create implementation of this repository? If yes, can I do it like spring is?
Inject
private final ConfigurableApplicationContext configurableApplicationContext;
into the class instantiating your desired class. Then call:
YourClass yourClass = (YourClass) configurableApplicationContext.getBeanFactory().initializeBean(new YourClass(), "YourClassName");
yourClass will then hold an instance of YourClass which has been initialized as if it was a Spring #Component (or a #RestController, which is the same from the dependency-injection point of view).
#SpringBootApplication
public class MainApplication extends SpringBootServletInitializer implements ApplicationContextAware {
private static ApplicationContext appContext;
public static void main(String[] args) {
SpringApplication.run(CrawlerApplication.class, args);
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.appContext = applicationContext;
}
....
MainApplication.getAppContext().getBean(CarRepository.class)

How to Pass #Value to a #Component for Spring Unit Testing

Im writing unit tests for services, controllers, etc however theres is a #Component that has the following values
#Component
Public class myclass
#Autowired
Private MyTemplate myTemplate
#Value("$someString")
Private String someString
#PostConstruct
Public void loadString()
...
How would I manually load values into the #Values? I have tried with Mocks, TestPropertySource, ReflectionTestUtils, among other ways found around
You can inject the #Value in test class by ReflectionTestUtils. Load container only in case of Controllers. For writing test cases for services and dao you don't need to load the spring container.
public class TestClass{
private #InjectsMock ServiceClass service;
#BeforeAll
public void setUp(){
ReflectionTestUtils.setField(service, "someString", "someValue");
}
//your test cases over here.
}
I can immediately think of two options
1) You could define $someString in your test/resources/test.properties
#RunWith(SpringRunner.class)
#TestPropertySource(locations="classpath:test.properties")
public class ClassTest {
}
2) do it manually
#RunWith(SpringRunner.class)
public class ClassTest {
#Autowired
private MyClass miclass;
#Before
public void setupObject() {
miclass.setProperty("someting");
}
}

How to mock a class that been annotated with Primary

I have :
an interface : EntityService
a first implementation : EntityServiceImpl - This class is annotated with #Primary
an other one : EntityServiceClientImpl
and a controller that has this field #Autowired EntityService
I would like to do a test on this controller and for this test to be unitary I mock EntityService.
So of course this code does not work because Spring detects two beans annotated with Primary :
#Configuration
class EntityControllerTestConfig {
#Bean
#Primary
EntityService entityService() {
return mock(EntityService.class);
}
}
#RunWith(SpringRunner.class)
#SpringBootTest(classes = TestApplication.class)
#WebAppConfiguration
#ContextConfiguration(classes = EntityControllerTestConfig.class)
public class EntityControllerTest {
#Autowired
private EntityService entityService;
...
#SpringBootApplication(scanBasePackages= "com.company.app")
#EntityScan (basePackages = {"com.company.app" }, basePackageClasses = {Jsr310JpaConverters.class })
#EnableJpaRepositories("com.company.app")
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
I tried to find an other way to mock and to exclude EntityServiceClient on test configuration but i was not able to mock. (cf : exclude #Component from #ComponentScan )
I finaly found that solution : a spring context (with controller, controllerAdvice and mock of service) and not a spring boot context
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class EntityControllerTest {
#Configuration
public static class EntityControllerTestConfig {
#Bean
public EntityService entityService() {
return mock(EntityService.class);
}
#Bean
public EntityController entityController() {
return new EntityController(entityService());
}
}
#Autowired
private EntityService entityService;
#Autowired
private EntityController entityController;
private MockMvc mockMvc;
#Before
public void setup() throws DomaineException {
this.mockMvc = MockMvcBuilders
.standaloneSetup(entityController)
.setControllerAdvice(new myControllerAdvice())
.build();
NB : Since Spring 4.2 you can set your ControllerAdvice like that.
You can approach it slightly differently and combine #WebMvcTest with #MockBean annotation to test just the controller with it's own minimal context.
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = EntityController.class)
public class EntityControllerTest {
#MockBean
private EntityService entityService;
#Autowired
private MockMvc mvc;
In this example EntityService will be mocked while MockMvc can be used to assert the request mappings in controller.

How enable/disable #EnableGlobalMethodSecurity for #Service methods for testing scenario

I am working with Spring Framework and Spring Security
About Testing
For a set of Test classes for #Controller with security, .apply(springSecurity() and #WithUserDetails(value="something") are used
#Before
public void setUp(){
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(springSecurity())// <---
.build();
}
For other set of Test classes for #Controller without security, therefore .apply(springSecurity()) and #WithUserDetails(value="something") are not used.
#Before
public void setUp(){
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.build();
}
Until here all about for #Controller with and without security work fine.
The problem is for the #Service, when #EnableGlobalMethodSecurity is defined and the #Service methods are annotated with #PreAuthorize("hasRole('ROLE_ADMIN')"), all the other Test classes for #Service where security is not required fail now with:
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException:
An Authentication object was not found in the SecurityContext
Of course it because the #Test methods do not use #WithUserDetails(value="something")
Thus, practically .apply(springSecurity()) does the job, but it for a Web environment through MockMvcBuilders.webAppContextSetup(webApplicationContext)
But for the server side, where security is not needed, I have:
#Transactional
#RunWith(Parameterized.class)
#ContextConfiguration(classes={RootApplicationContext.class})
#ActiveProfiles(resolver=TestActiveProfilesResolver.class)
#TestExecutionListeners(listeners={LoggingTestExecutionListener.class}, mergeMode=MergeMode.MERGE_WITH_DEFAULTS)
public class PersonaServiceImplTest {
private static final Logger logger = LoggerFactory.getLogger(PersonaServiceImplTest.class.getSimpleName());
#ClassRule
public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();
#Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
#Autowired
private Environment environment;
...
Thus MockMvcBuilders.webAppContextSetup(webApplicationContext) has no sense to be used. What is the best way to resolve this?
You can use #WithUserDetails and #WithMockUser to test method security as well.
For the tests to pick up on method security, you need to include the class annotated with #EnableGlobalMethodSecurity in the component classes used for loading the ApplicationContext.
For example, if the configuration class SecurityConfig is annotated with EnableGlobalMethodSecurity
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig { }
And the Service MessageService has a method using #PreAuthorize.
#Service
public class MessageService {
public String getHelloMessage() {
return "Hello!";
}
#PreAuthorize("hasRole('ADMIN')")
public String getGoodbyeMessage() {
return "Goodbye!";
}
}
Then you need to include both of those classes in the MessageServiceTest and you can use the security testing annotations.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = {SecurityConfig.class, MessageService.class})
public class MessageServiceTest {
#Autowired
MessageService messageService;
#Test
public void helloMessageReturnsHello() {
assertThat(messageService.getHelloMessage()).isEqualTo("Hello!");
}
#Test(expected = AuthenticationCredentialsNotFoundException.class)
public void goodbyeMessageWithoutUserThrowsException() {
messageService.getGoodbyeMessage();
}
#WithMockUser(roles = "ADMIN")
#Test
public void goodbyeMessageWithAdminReturnsGoodbye() {
assertThat(messageService.getGoodbyeMessage()).isEqualTo("Goodbye!");
}
}

How can I test a destroy method of a bean is effectively called in a springboot integration test?

My configuration class looks like this :
#SpringBootApplication
public class Application {
#Bean(destroyMethod = "close")
public CassandraClient cassandraClient() { ... }
}
My CassandraClient class has a close() method, which is being invoked when the application context shuts down (I see it through step debugging). However, I can't find a way to test that the close() method is effectively called.
Here is what I would like to test :
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
#ContextConfiguration(classes = { Application.class })
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class ApplicationIntegrationTests implements ApplicationContextAware {
ApplicationContext applicationContext;
#Autowired
CassandraClient cassandraClient;
#Test
public void cassandraClientCloseIsCalled() {
((ConfigurableApplicationContext)applicationContext).close();
// How can I check that cassandraClient.close() has been called once ?
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
I tried adding an aspect to my configuration class to do the counting, but I can't get a pointcut to match the close method. It seems like my aspect is being destroyed before the cassandraClient bean.
I see this question has been here for a while, I looked into this and found a solution that works for me. You can look at it here: Testing Nuts Example. Essentially, almond is a nut that I create using the nut object. Both the init and the destroy methods are private, so I create an extension in another configuration in order to create a mock and be able to verify it with Mockito. This way I can shadow the private methods and generate public methods for this test. But what I think it's interesting in this case for you is that using the #PreDestroy annotation in my #Configuration for the #Test, I can then test the destroy method, just because at that point, all destroy methods have been called. Here is a copy of my code here just to clarify this:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {NutsConfiguration.class, NutsMethodsConfigurationTest.NutsTestConfiguration.class})
class NutsMethodsConfigurationTest {
#Autowired
public Nut almond;
#MockBean
public static NutExtended nutExtended;
#Autowired
public ApplicationContext applicationContext;
#Test
void almond() {
ConsolerizerComposer.outSpace()
.orange(almond)
.orange(nutExtended)
.reset();
verify(nutExtended, times(1)).initiate();
}
#Configuration
public static class NutsTestConfiguration {
#Bean
#Primary
#Qualifier("nut")
public NutExtended nut() {
return new NutExtended();
}
#PreDestroy
public void check() {
verify(nutExtended, times(1)).goToCake();
}
}
#Configuration
public static class NutExtended extends Nut {
public void goToCake() {
BROWN.printGenericLn("Going to cake...");
}
public void initiate() {
ORANGE.printGenericLn("Creating %s", toString());
}
}
}
I hope this helps 😊!

Resources