Do java validation annotations run before AOP - spring

I'm new in Spring Boot AOP.
Does an AOP method annotated with #Before run before java validation annotations (such as #NotNull)?
I have some other custom validations that need to run for every request but I need to run these validations after java validation annotations run.
Which one will run first?
my Controller:
#RestController
#RequestMapping("/users")
public class UserController {
private final UserService userService;
#Autowired
public UserController(UserService userService) {
this.userService = userService;
}
#PostMapping(value = "")
public List<User> getAllUsers(#Valid #RequestBody User user) {
return userService.getAllUsers();
}
}
and my advice:
#Aspect
#Component
public class AspectConfig {
#Pointcut(value = "within(#org.springframework.web.bind.annotation.RestController *)")
public void restControllers() {
}
#Before(value = "restControllers()")
public void logRequest(JoinPoint joinPoint) {
...
}
}

Does an AOP method annotated with #Before run before java validation annotations
No, it runs afterwards, just like you wish. See also this question. So you should be all set. Your logging advice should only be triggered if validation was successful, because only then the target method will be called.
You can implement a HandlerInterceptor if you wish to log/do something on the request level before validators kick in, see here.

Related

Is there a way to test nested objects without the web or persistence layer in Spring Boot?

I'm using JUnit5 to test a Spring Boot application. I want to test a #Service object, which uses #Autowired fields. I would like to mock another #Service object which is indirectly used by my test object. Concretely, I have the following (highly simplified) setup:
Object being tested:
#Service
public class MainService {
private #Autowired SubService subService;
public String test() {
return subService.test();
}
}
SubService:
#Service
public class SubService {
private #Autowired StringService stringService;
public String test() {
return stringService.test();
}
}
StringService:
#Service
public class StringService {
public String test() {
return "Real service";
}
}
Test class used:
#SpringBootTest
public class MainServiceTest {
private #Autowired MainService mainService;
private #MockBean StringService stringService;
#BeforeEach
public void mock() {
Mockito.when(stringService.test()).thenReturn("Mocked service");
}
#Test
public void test() {
assertEquals("Mocked service", mainService.test());
}
}
The above works if I run the test class as a #SpringBootTest, but this loads the full application and is very slow. I also want to avoid #WebMvcTest since I don't need the web server, or #DataJpaTest since I don't need persistence. I don't want to mock SubService, as it contains functionality I want to test together with the MainService.
I tried the following:
#ExtendWith(SpringExtension.class) => throws NoSuchBeanDefinitionException, it seems the autowiring does not work in this case
#ExtendWith(MockitoExtension.class) and using #InjectMocks and #Mock instead of the Spring annotations => as the StringService is not a direct field of the MainService being tested, this does not work.
Is there a way to use the spring dependency injection system without loading the web server or persistence layer, or alternatively not use Spring tests but allow for 'nested' dependency injection?
You can use profiling (i.e Spring #Profile) to avoid loading the whole application. It will look something like below:
#Profile("test")
#Configuration
public class TestConfiguration {
#Bean
public MainService mainService() {
return new MainService();
}
#Bean
public SubService subService() {
return new SubService();
}
// mock the StringService
#Bean
public StringService stringService() {
return Mockito.mock(StringService.class);
}
}
then use that profile with `#SpringBootTest(classes = TestConfiguration.class), it will look something like below:
#ActiveProfiles("test")
#SpringBootTest(classes = TestConfiguration.class)
class MainServiceTest {
#Autowired
private MainService mainService;
#Test
public void test() {
// configure behavior using apis like when(), basically however you
// want your mock to behave
}
}
This will load only the beans defined in the class TestConfiguration.
NOTE: Since your question is more about how to avoid loading the whole application, I've answered focusing on that. The above approach will get the job done, but I'd prefer constructor wiring over any other mode of dependency injection on any given day, it's easier to maintain and test(like cases where you want to mock).

How to include custom security interceptor in spring boot test

I want to do some end-to-end test for spring boot rest-api application. To achieve this im using spring mock mvc. But i can't get the 200 response because the rest api is using custom security interceptor to validate the token in request. Instead i keep getting 401 as a response. How to include this token validation in my test?
I've tried several configuration by including #ContextConfiguration(classes = {WebMvcConfig.class}) in my test class. WebMvcConfig is configuration class to register the interceptor.
This is my test file
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
#SpringBootTest(classes = VeripalServiceApplication.class)
#TestPropertySource(locations="classpath:test.properties")
#Transactional
public class VeripalTextConfigurationTest {
#Autowired
private MockMvc mockMvc;
#Test
public void happpyPath_thenReturns200() throws Exception {
String jsonBody = "some json body";
String endPoint = "/end_point_to_my_api";
HttpHeaders headers = new HttpHeaders();
headers.add("token", "this_is_my_token");
headers.setContentType(aplication/json);
/** Hit the API */
mockMvc.perform(post(endPoint)
.headers(httpHeaders)
.content(jsonBody)
)
.andExpect(status().isOk()).andDo(print());
}
}
And this is the #Configuration
#Configuration
#EnableScheduling
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Autowired
private ConsumerService consumerService;
#Autowired
private EndpointService endpointService;
#Autowired
private ConsumerConfigurationService consumerConfigurationService;
#Autowired
private AccessLimitService accessLimitService;
#Autowired
private ConfigurationHistoryService configurationHistoryService;
#Autowired
private LimitCarryOverService limitCarryOverService;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new Interceptor(consumerService, endpointService, consumerConfigurationService, accessLimitService, configurationHistoryService, limitCarryOverService));
}
}
And this is my Interceptor class
public class Interceptor implements HandlerInterceptor {
// some code here ...
}
you need to have a clear picture of request life-cycle in Servlet API and Spring Security framework.
This article might help you to understand this flow http://blog.florian-hopf.de/2017/08/spring-security.html
So, I'm pretty sure, you have an issue in authentication filters, thus you can resolve it in couple ways:
Disable security, for example by using #AutoConfigureMockMvc(secure = false)
Or you need to mock some places (AuthenticationProvider, UserDetailsService, etc) where you can provide Authentication object
Or, it also might help, try to play with #WithMockUser
.
Related posts:
Spring Test & Security: How to mock authentication?
V2: use IoC + Mockito, e.g. stub it for unit tests. I don't see how your code are written, so I believe a snippet below might help you.
// #Import({MyAuthCustomInterceptor.class}) // eq to #Component/#Service to create a bean
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Autowired
MyAuthCustomInterceptor interceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor);
}
}
public class VeripalTextConfigurationTest {
#MockBean
MyAuthCustomInterceptor interceptor;
#SetUp
public void setup(){
Mockito.when(interceptor.preHandle(...)).thenReturn(true);
}
}

Transactional and Stream in Spring

I try to understand why this code doesn't work
In component:
#PostConstruct
public void runAtStart(){
testStream();
}
#Transactional(readOnly = true)
public void testStream(){
try(Stream<Person> top10ByFirstName = personRepository.findTop10ByFirstName("Tom")){
top10ByFirstName.forEach(System.out::println);
}
}
And repository :
public interface PersonRepository extends JpaRepository<Person, Long> {
Stream<Person> findTop10ByFirstName(String firstName);
}
I get:
org.springframework.dao.InvalidDataAccessApiUsageException: You're trying to execute a streaming query method without a surrounding transaction that keeps the connection open so that the Stream can actually be consumed. Make sure the code consuming the stream uses #Transactional or any other way of declaring a (read-only) transaction.
One key thing about Spring is that many annotated features use proxies to provide the annotation functionality. That is #Transactional, #Cacheable and #Async all rely on Spring detecting those annotations and wrapping those beans in a proxy bean.
That being the case, a proxied method can only be used when invoked on the class and not from within the class. See this about the topic.
Try:
Refactoring and call this #Transactional method from another class in your context, or
By self-autowiring the class into itself and calling the #Transactional method that way.
To demonstrate (1):
public class MyOtherClass {
#Autowired
private MyTestStreamClass myTestStreamClass;
#PostConstruct
public void runAtStart(){
// This will invoke the proxied interceptors for `#Transactional`
myTestStreamClass.testStream();
}
}
To demonstrate (2):
#Component
public class MyTestStreamClass {
#Autowired
private MyTestStreamClass myTestStreamClass;
#PostConstruct
public void runAtStart(){
// This will invoke the proxied interceptors for `#Transactional` since it's self-autowired
myTestStreamClass.testStream();
}
#Transactional(readOnly = true)
public void testStream(){
try(Stream<Person> top10ByFirstName = personRepository.findTop10ByFirstName("Tom")){
top10ByFirstName.forEach(System.out::println);
}
}
}

Spring Boot | MyBatis project structure can't wire layers

Can't wire layers in Spring Boot | MyBatis application. The problem is probably happening when Service layer uses Mapper.
Controller method sample:
#Controller
#RequestMapping("demo")
public class MessageController {
#Autowired
private MessageService messageService;
#RequestMapping(value = "messages", method = RequestMethod.GET)
public String getMessages(ModelMap modelMap) {
modelMap.addAttribute(MESSAGE,
messageService.selectMessages());
return "messages";
}
Service class:
#Service
public class MessageService {
#Autowired // Not sure if I can use Autowired here.
private MessageMapper messageMapper;
public MessageService() {
}
public Collection<Message> selectMessages() { return
messageMapper.selectAll(); }
}
MyBatis Mapper:
#Mapper
public interface MessageMapper {
#Select("select * from message")
Collection<Message> selectAll();
}
UPDATE
It feels like I'm having some fundamental knowledge based mistake. Probably managing external libraries.
Here's maven pom.xml. Looks kind of overloaded, I faced a lot of errors managing different spring-boot packages. Starter for autoconfiguration included.
pom.xml
Here's the project structure:
UPDATE #2
I'm sure DB connection is working well, I'm able to track changes in MySQL Workbench while Spring Boot is executing schema.sql and data.sql. But somehow, MyBatis mapper methods throw NullPointerException and page proceeds with exit code 500. Seems like they can't connect.
MessageService isn't managed by spring.
You have to annotate the MessageService class with #Service annotation (also, after adding this annotation you can indeed use #Autowired inside the service class)
#Service
public class MessageService {
#Autowired
private MessageMapper messageMapper;
public Collection<Message> selectMessages() {
return messageMapper.selectAll();
}
}
and wire it to the controller with
#Autowired
private MessageService messageService
and use it in a method like this
#RequestMapping(value = "messages", method = RequestMethod.GET)
public String getMessages(ModelMap modelMap) {
modelMap.addAttribute(MESSAGE, messageService.selectMessages());
return "messages";
}

Mocking a service within service (JUnit)

I have the following service:
#Service
public class PlayerValidationService {
#Autowire
private EmailService emailService;
public boolean validatePlayerEmail(Player player) {
return this.emailService.validateEmail(player.getEmail());
}
Now in my junit test class i'm using a different 3rd service that uses PlayerValidationService :
public class junit {
#autowire PlayerAccountService playerAccountService ;
#Test
public test() {
this.playerAccountService .createAccount();
assertAllSortsOfThings();
}
Is it possible to mock the EmailService within the PlayerAccountService when using annotation based autowiring? (for example make the mock not checking the validation of the email via the regular email provider we work with)
thanks.
There are a couple of ways in which you could do this. First the simplest option is to ensure that your service provides a setEmailService(EmailService) method. In which case you just replace the Spring-injected implementation with your own.
#Autowired
private PlayerValidationService playerValidationService;
#Mock
private EmailService emailService;
#Before
public void setup() {
initMocks(this);
playerValidationService.setEmailService(emailService);
}
A shortcoming of that approach is that an instance of the full-blown EmailService is likely to be created by Spring. Assuming that you don't want that to happen, you can use 'profiles'.
In your test packages, create a configuration class which is only active in a particular profile:
#Configuration
#Profile("mockemail")
public class MockEmailConfig {
#Bean(name = "emailService")
public EmailService emailService() {
return new MyDummyEmailService();
}
}
And add an annotation to your test to activate that profile:
#ActiveProfiles({ "mockemail" })
public class PlayerValidationServiceTest {
//...
}

Resources