#EnableTransactionManagement breaks my tests - spring

So, I have a Spring Boot app. Recently I added an aspect I would like to apply after #Transaction aspect handler. So on my main app class I added #EnableTransactionManagement with lowering the order for transactions.
#SpringBootApplication
#EntityScan(basePackageClasses = { Application.class, Jsr310JpaConverters.class })
#EnableAutoConfiguration(exclude = RepositoryRestMvcAutoConfiguration.class)
#EnableSpringDataWebSupport
#EnableScheduling
// Need to lower the transaction aspect priority in order to SyncAspect have a higher prio
// The lower the number the higher the priority, defaults to HIGHEST_PRECEDENCE = -2147483648
#EnableTransactionManagement(order = Ordered.HIGHEST_PRECEDENCE + 2)
public class Application extends SpringBootServletInitializer {
public static void main(final String[] args) {
SpringApplication.run(Application.class, args);
}
...
The order here does not matter (the test fails even when I just add #EnableTransactionManagement without any additional config) but after adding this annotation one of our tests failed. It is a #WebMVCTest and now it returns 404 for all the requests made in the test. Here is the test class declared.
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = OptionMenuController.class)
#WithMockUser(roles = { "ENTERPRISE_USER" })
#ActiveProfiles(profiles = {"develop"})
public class OptionMenuControllerTest extends AbstractFullFeatureSetControllerTest<OptionMenu> {
...
#Import(DummySecurityConfiguration.class)
public abstract class AbstractFullFeatureSetControllerTest<EntityType extends Identifiable>
extends AbstractControllerTest<EntityType>
...
#TestConfiguration
public class DummySecurityConfiguration {
#Bean(name = "store_based_auth")
public UserDetailsService storeBasedAuthService() {
return Mockito.mock(UserDetailsService.class);
}
#Bean(name = "webui")
public UserDetailsService webUiService() {
return Mockito.mock(UserDetailsService.class);
}
#Bean(name = "integration_api_auth")
public UserDetailsService integrationApiAuthService() {
return Mockito.mock(UserDetailsService.class);
}
#Bean(name = "onboarding_api_auth")
public UserDetailsService onboardingApiAuthService() {
return Mockito.mock(UserDetailsService.class);
}
#Bean
public JwtService jwtService() {
return Mockito.mock(JwtService.class);
}
}
I have no idea why it happens. It looks really strange. But my guess is it has something to do with this #WithMockUser annotation. Maybe when I added #EnableTransactionManagement on the main class it reconfigured something in a wrong way that a dummy user is not found any more that's why I get 404 for all the tests in the test class.
Any ideas or hints?
While debugging I found that the controller method is not ever run.

Related

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!");
}
}

Cannot Mock an Injected Dependency in Controller Constructor

I am having a problem with mocking an object being injected in to a controller.
I am running an integration test. In my test I have the following setup:
#RunWith(SpringRunner.class)
#SpringBootTest
public class AuthenticationTests {
#TestConfiguration
public class Config {
#Bean
#Primary
public AbstractClient client() {
return new AbstractClient() {
#Override
public ManagedChannel getChannel() {
return new ManagedChannel();
}
};
}
}
}
In the controller being tested, AbstractClient is dependency injected like so:
#Controller
public class MyController {
private ManagedChannel managedChannel;
public MyController(AbstractClient client) {
managedChannel = client.getChannel();
}
}
Whenever I run the test, AbstractClient defined in the #TestConfiguration class is never injected - instead the default one is (which is annotated as #Service).
Can anyone help?

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 😊!

Mocking beans in spring context using Spring Boot

I'm using Spring Boot 1.3.2, and I notice problem, ComponentScan in my test class is not working. And I want to mock some of Spring Beans. Is spring boot blocking ComponentScan?
Test config class:
#Configuration
#ComponentScan(value = {"myapp.offer", "myapp.image"})
public class TestEdge2EdgeConfiguration {
#Bean
#Primary
public OfferRepository offerRepository() {
return mock(OfferRepository.class);
}
}
Test class:
#ContextConfiguration(classes=TestEdge2EdgeConfiguration.class, loader=AnnotationConfigContextLoader.class)
public class OfferActionsControllerTest extends AbstractTestNGSpringContextTests {
#Autowired
private OfferRepository offerRepository;
#Autowired
private OfferActionsController offerActionsController;
#BeforeMethod
public void setUp(){
MockitoAnnotations.initMocks(this);
}
#Test
public void saveOffer() {
//given
BDDMockito.given(offerRepository.save(any(Offer.class))).willReturn(new Offer());
//when
ResponseEntity<Offer> save = offerActionsController.save(new Offer());
//then
org.springframework.util.Assert.notNull(save);
}
}
Solution of this problem
Test config class, it's important to exclude SpringBootApplicationConfigutation and add #ComponentScan:
#ComponentScan(basePackages = "com.example", excludeFilters =
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,
value = {SpringBootApplicationConfigutation.class, MyDao.class}
)
)
#Configuration
public class TestEdge2EdgeConfiguration {
#Bean
public OfferRepository offerRepository() {
return mock(OfferRepository.class);
}
}
And test:
#SpringApplicationConfiguration(classes=TestEdge2EdgeConfiguration.class)
public class OfferActionsControllerTest extends AbstractTestNGSpringContextTests {
#Autowired
private OfferRepository offerRepository;
#Autowired
private OfferActionsController offerActionsController;
#BeforeMethod
public void resetMock() {
Mockito.reset(offerRepository);
}
#Test
public void saveOffer() {
//given
BDDMockito.given(offerRepository.save(any(Offer.class))).willReturn(new Offer());
//when
ResponseEntity<Offer> save = offerActionsController.save(new Offer());
//then
org.springframework.util.Assert.notNull(save);
}
}

Spring boot #Transactional doesn't work

I had add #Transactional on the method in service layer.
#Transactional(readOnly = false)
public void add(UserFollow uf){
UserFollow db_uf = userFollowRepository.findByUserIdAndFollowUserId(uf.getUserId(), uf.getFollowUserId());
if(db_uf == null) {
userFollowRepository.save(uf);
userCountService.followInc(uf.getFollowUserId(), true);
userCountService.fansInc(uf.getUserId(), true);
throw new RuntimeException();// throw an Exception
}
}
userFollowRepository.save(uf); still save seccessful,doesn't rollback...
i enable transaction manager on the Application.
#Configuration
#ComponentScan
#EnableAutoConfiguration
#EnableJpaRepositories
#EnableTransactionManagement
public class Application {
#Bean
public AppConfig appConfig() {
return new AppConfig();
}
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
i move #Transactional to Control layer, it works, the code:
#Transactional
#RequestMapping(value="following", method=RequestMethod.POST)
public MyResponse follow(#RequestBody Map<String, Object> allRequestParams){
MyResponse response = new MyResponse();
Integer _userId = (Integer)allRequestParams.get("user_id");
Integer _followUserId = (Integer)allRequestParams.get("follow_user_id");
userFollowService.add(_userId, _followUserId); //this will throw an exception, then rollback
return response;
}
can anyone tell me reason, thanks!
According to http://spring.io/guides/gs/managing-transactions/
#EnableTransactionManagement activates Spring’s seamless transaction features, which makes #Transactional function
so it started to work after you had added #EnableTransactionManagement

Resources