No further requests expected while MockRestServiceServer was set to ExpectedCount.manyTimes() - spring

I have following test class for my spring-integration application that passes sucessfully being launched alone
#SpringBootTest(classes = {BackupTestDefinition.class})
#ActiveProfiles({"test", "dev"})
#RunWith(SpringRunner.class)
public class BackupServiceTest {
#Value(value = "${ne.endpoint}")
private String ne;
#Autowired
private RestTemplate restTemplate;
private MockRestServiceServer mockServer;
#Before
public void setup() {
mockServer = MockRestServiceServer.bindTo(restTemplate).build(new UnorderedRequestExpectationManager());
mockServer.expect(ExpectedCount.manyTimes(), requestTo(UriComponentsBuilder.fromHttpUrl(ne).build().toUri())).andExpect(method(HttpMethod.POST)).andRespond(withSuccess());
}
#Test
public void testNotificationProcessing() throws IOException, InterruptedException, InitializationException, ExecutionException {
//some testing code
}
}
But I have another test that has other settings (ExpectedCount.times(1)) for the same endpoint and has different TestDefinition. So there are couple of contexts cached in this test suite. And when I launch them together I receive following exception
at org.springframework.test.web.client.AbstractRequestExpectationManager.createUnexpectedRequestError(AbstractRequestExpectationManager.java:141)
at org.springframework.test.web.client.UnorderedRequestExpectationManager.validateRequestInternal(UnorderedRequestExpectationManager.java:49)
at org.springframework.test.web.client.AbstractRequestExpectationManager.validateRequest(AbstractRequestExpectationManager.java:76)
at org.springframework.test.web.client.MockRestServiceServer$MockClientHttpRequestFactory$1.executeInternal(MockRestServiceServer.java:289)
at org.springframework.mock.http.client.MockClientHttpRequest.execute(MockClientHttpRequest.java:94)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:659)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:620)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:538)
Caused by: java.lang.AssertionError: No further requests expected: HTTP POST
After hours of debugging I found out that settings were successfully applied, but looks like restTemplate was called from another context where number of attempts was exhausted. Can you please help me to find out how this issue can be resolved.

This issue can be resolved using #DirtiesContext on the test class, alongside with the #RunWith:
* Test annotation which indicates that the
* {#link org.springframework.context.ApplicationContext ApplicationContext}
* associated with a test is <em>dirty</em> and should therefore be closed
* and removed from the context cache.
*
* <p>Use this annotation if a test has modified the context — for
* example, by modifying the state of a singleton bean, modifying the state
* of an embedded database, etc. Subsequent tests that request the same
* context will be supplied a new context.
*
* <p>{#code #DirtiesContext} may be used as a class-level and method-level
* annotation within the same class or class hierarchy. In such scenarios, the
* {#code ApplicationContext} will be marked as <em>dirty</em> before or
* after any such annotated method as well as before or after the current test
* class, depending on the configured {#link #methodMode} and {#link #classMode}.
*
And here are Docs on the matter: https://docs.spring.io/spring/docs/5.0.6.RELEASE/spring-framework-reference/testing.html#dirtiescontext

Related

Why DirtiesContext is needed on other test classes to mock bean dependency for class with JMS Listener

Context
A Spring Boot application with a Rest endpoint and a JMS AMQ Listener
Test behaviour observed
The tests classes run fine without needing DirtiesContext individually but when the entire suite of test classes are run the following behaviours are observed -
Mocking of a bean dependency for the JMS Consumer test requires the earlier test classes to have a DirtiesContext annotation.
Mocking of bean dependency for RestControllers seem to work differently than a JMS Listener i.e don't need DirtiesContext on the earlier test classes
I've created a simple Spring application to reproduce the Spring context behaviour I need help understanding - https://github.com/ajaydivakaran/spring-dirties-context
The reason this happens is due to the fact that without #DirtiesContext Spring will remain the context for reuse for other tests that share the same setup (read more on Context Caching in the Spring documentation). This is not ideal for your setup as you have a messaging listener, because now multiple Spring Contexts can remain active and steal the message you put into the queue using the JmsTemplate.
Using #DirtiesContext ensures to stop the application context, hence this context is not alive afterward and can't consume a message:
from #DirtiesContext:
Test annotation which indicates that the {#link org.springframework.context.ApplicationContext ApplicationContext} *
associated with a test is dirty and should therefore be
closed and removed from the context cache.
For performance reasons, I would try to not make use of #DirtiesContext too often and rather ensure that the JMS destination is unique for each context you launch during testing. You can achieve this by outsourcing the destination value to a config file (application.properties) and randomly populate this value e.g. using a ContextInitializer.
A first (simple) implementation could look like the following:
#AllArgsConstructor
#Service
public class Consumer {
private EnergeticGreeter greeter;
private MessageRepository repository;
private ApplicationContext applicationContext;
#JmsListener(destination = "${consumer.destination}")
public void consume(
#Header(name = JmsHeaders.MESSAGE_ID, required = false) String messageId,
TextMessage textMessage) {
System.out.println("--- Consumed by context: " + applicationContext.toString());
if ("Ahem hello!!".equals(greeter.welcome().getContent())) {
repository.save();
}
}
}
the corresponding test:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ContextConfiguration(initializers = DestinationValueInitializer.class)
public class JMSConsumerIntegrationTest {
#Autowired
private JmsTemplate jmsTemplate;
#Value("${consumer.destination}")
private String destination;
#Autowired
private ApplicationContext applicationContext;
#MockBean
private EnergeticGreeter greeter;
#MockBean
private MessageRepository repository;
//Todo - To get all tests in this project to pass when entire test suite is run look at Todos added.
#Test
public void shouldInvokeRepositoryWhenGreetedWithASpecificMessage() {
when(greeter.welcome()).thenReturn(new Message("Ahem hello!!"));
System.out.println("--- Send from context: " + applicationContext.toString());
jmsTemplate.send(destination, session -> session.createTextMessage("hello world"));
Awaitility.await().atMost(10, TimeUnit.SECONDS).untilAsserted(
() -> verify(repository, times(1)).save()
);
}
}
and the context initializer:
public class DestinationValueInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
TestPropertyValues.of("consumer.destination=" + UUID.randomUUID().toString()).applyTo(applicationContext);
}
}
I've provided a small PR for your project where you can see this in the logs, that a different application context is consuming your message and hence you can't verify that the repository was called on the application context you write your test in.

Exception "At least one JPA metamodel must be present" thrown in Controller-Test

I am trying to test a controller class annotated with #RestController. I am using Spring-Boot 1.5.10.
The application itself starts up properly, but the unit test fails. Please bear in mind, that I am currently just trying to test the controller (and mock away the service - I will be testing the services later).
Here are some of my classes:
Application.java
package com.particles.authservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
#SpringBootApplication
#Import({ ApplicationConfiguration.class })
public class Application {
public static void main(final String[] args) {
SpringApplication.run(Application.class, args);
}
}
ApplicationConfiguration.java
package com.particles.authservice;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.convert.threeten.Jsr310JpaConverters;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#EntityScan(basePackageClasses = { Application.class, Jsr310JpaConverters.class })
#EnableJpaRepositories
public class ApplicationConfiguration {
}
AccountController.java
package com.particles.authservice.accountservice;
import ...
#RestController
public class AccountController {
#Autowired
private AccountService accountService;
/**
* This method attempts to login a user and issue a token if the login was successful.
* <p>
* If login fails due a login attempt with a non-existent username or an invalid password, an exception is thrown.
*
* #param credentials
* ({#link Credentials}) credentials in a JSON-form, that can be unserialized into an object of this type
* #param response
* ({#link HttpServletResponse}) response, which will be sent to the client;
* if the credentials were valid the response receives a JWT as an additional header
* #return ({#link PersistableAccount}) JSON (automatically serialized from the given TO);
* if the request was successful, an additional header containing the freshly issued JWT is added into the response
*/
#RequestMapping(value = "/login", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public PersistableAccount login(#RequestBody final Credentials credentials, final HttpServletResponse response)
throws IOException, URISyntaxException {
final Optional<PersistableAccount> account = accountService.login(credentials);
if (!account.isPresent()) {
throw new AccountLoginFailedException(credentials);
}
response.setHeader("Token", jwtService.tokenForPersistableAccount(account.get()));
return account.get();
}
}
AccountControllerTest.java
package com.particles.authservice;
import static ...
import ...
#RunWith(SpringRunner.class)
#WebAppConfiguration
#WebMvcTest(AccountController.class)
public class AccountControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private AccountService accountServiceMock;
#Test
public void test() throws Exception {
final Credentials credentials = TestHelper.createCredentials();
final Optional<PersistableAccount> account = Optional.of(TestHelper.createPersistableAccount());
given(accountServiceMock.login(credentials))
.willReturn(account);
mockMvc
.perform(MockMvcRequestBuilders.post("/login").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
I have reduced the AccountController to just one endpoint and omitted imports for brevity purposes.
The test compiles just fine, but whenever I run the test, I receive the following (nested) exception (shortened - let me know if you need the full stacktrace):
Caused by: java.lang.IllegalArgumentException: At least one JPA metamodel must be present!
at org.springframework.util.Assert.notEmpty(Assert.java:277)
at org.springframework.data.jpa.mapping.JpaMetamodelMappingContext.<init>(JpaMetamodelMappingContext.java:52)
at org.springframework.data.jpa.repository.config.JpaMetamodelMappingContextFactoryBean.createInstance(JpaMetamodelMappingContextFactoryBean.java:71)
at org.springframework.data.jpa.repository.config.JpaMetamodelMappingContextFactoryBean.createInstance(JpaMetamodelMappingContextFactoryBean.java:26)
at org.springframework.beans.factory.config.AbstractFactoryBean.afterPropertiesSet(AbstractFactoryBean.java:134)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
... 40 more
I have checked out a lot of similar questions, but the usual ways to resolve this did not seem to work in my case. In particular I tried the following:
Using spring-boot-starter-data-jpa (did not apply to me since I was using that dependency to begin with), managed version
Separating application from its (JPA-related) configuration due to problems in regards to #EnableJpaRepositories and possibly #EntityScan - to no avail (following the first reply in Getting "At least one JPA metamodel must be present" with #WebMvcTest, but while my application still starts just fine, the test still fails)
I have tried using JacksonTester - in fact I just want to test the controller functionality at the moment - to no avail either (ended up needing the context)
As far as I understand I am mocking away the actual service; so in fact I am not using any JPA Metamodels, am I?
Removing the #EnableJpaRepositories annotation solves the issue, but unfortunately it seems to break my application.
What am I doing wrong?
Add ContextConfiguration. The test did not see ApplicationConfiguration, hence did not see any entity.
#ContextConfiguration(classes = {ApplicationConfiguration.class})
public class AccountControllerTest { ... }
Update:
Another thing the code is missing is #SpringBootTest. Try annotating the test class with this one.

Testing Spring JAX-WS Web Services

I have a Web Service (#WebService) exported with SimpleJaxWsServiceExporter.
I want to create integration tests for it.
How can I do this? Is possible to use the MockWebServiceClient class from the spring-ws-test project?
No, the MockWebServiceClient works with Spring-WS services only.
You either can create client-side or server-side test cases.
I prefer server side test as you do not have to delploy your application at all. It should be an out-of-container test.
SEREVR SIDE Spring-WS Test (NO DEPLOYMENT REQUIRED) - Keep in mind the following points.
import org.springframework.ws.test.server.MockWebServiceClient
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = YOUR CONTEXT XML FILE LOCATION
/**
* <strong>Main entry point for server-side Web service testing</strong>. Typically used to test a {#link
* org.springframework.ws.server.MessageDispatcher MessageDispatcher} (including its endpoints, mappings, etc) by
* creating request messages, and setting up expectations about response messages.
*/
private MockWebServiceClient mockServiceClient;
#Before
public void setUp() throws Exception{
mockServiceClient = MockWebServiceClient.createClient(applicationContext);
}
#Test
mockServiceClient.sendRequest(withPayload(VALID_PAYLOAD_REQUEST)).andExpect(VALID_PAYLOAD_RES)
CLIENT SIDE Spring-WS TEST - YOU HAVE TO DEPLOY EAR or WAR
public class CountryServiceClient extends WebServiceGatewaySupport
String uri = "http://localhost:8080/Endpointaddress/url
request = User.class/ anythig else/ make the bean ready
Object response = getWebServiceTemplate().marshalSendAndReceive(uri, request, new WebServiceMessageCallback() {
//real call to webservice make sure your application is up and running with the above uri.
#Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
SaajSoapMessage soapMessage = (SaajSoapMessage) message;
MimeHeaders mimeHeader = soapMessage.getSaajMessage().getMimeHeaders();
mimeHeader.setHeader("ApplicationId", "Z1012922");
mimeHeader.setHeader("userid", "aacom");
}
});

Spring Boot application is ignoring java config

I have a fairly simple Spring Boot app I am working on that uses a few Java Config classes. However, it seems that the configuration is not being picked up. I have break points all over, but nothing gets tripped. I even tossed a few RuntimeExceptions just to see if maybe my debugger was on the fritz.
In my main class, I have the standard Spring Boot main:
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
As you can see, I tagged it with #ComponentScan and #EnableAutoConfiguration. The Application class lives at the root of the classpath. My understanding of the #ComponentScan annotation is that it will search for all configuration classes beneath it.
In a package one layer down I have all the config classes:
My "Common" configuration
#Configuration
#EnableJpaRepositories("com.codechimp.XXX.repository")
#EnableTransactionManagement
public class AppCommonConfig {
#Inject
private Environment environment;
/* Define common beans here like datasource and such */
}
And my Spring Security configuration
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Inject
private LocalXXXUserDetailsService localXXXUserDetailsService;
/**
* #see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(HttpSecurity)
*/
#Autowired
protected void configure(HttpSecurity http) throws Exception {
// Configure http
}
/**
* #see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configureGlobal(AuthenticationManagerBuilder)
*/
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
// Configure auth
}
}
However, when I run the app, it doesn't seem to call any of the methods in either of these config classes. It's as if they are being completely ignored. As I said, I have tried setting break points and even throwing a RuntimeException right in the beginning of all the methods like so:
if (true)
throw new RuntimeException("Break!");
Admittedly I have not had much experience with using Java Config, but I have been over the docs again and again and I am not seeing the missing piece(s).
I think you need your Application to be a #Configuration.
It's not a great idea to do a #ComponentScan from the default package (I assume that's what you mean by "the root of the classpath"). That would definitely switch some things off, but more seriously it causes a huge scan of all jars on your classpath, which is not a great idea (and can cause the app to fail).
You need to add
#SpringBootApplication
to your Spring Boot main class. From the docs:
/**
* Indicates a {#link Configuration configuration} class that declares one or more
* {#link Bean #Bean} methods and also triggers {#link EnableAutoConfiguration
* auto-configuration} and {#link ComponentScan component scanning}. This is a convenience
* annotation that is equivalent to declaring {#code #Configuration},
* {#code #EnableAutoConfiguration} and {#code #ComponentScan}.
*
* #author Phillip Webb
* #author Stephane Nicoll
* #since 1.2.0
*/

java.lang.IllegalArgumentException: error at ::0 can't find referenced pointcut

I'm really very new to Spring AOP. In my application, I had configured HiddenHttpMethodFilter that converts method parameters into HTTP methods and enables Spring to handle other HTTP methods like DELETE, PUT etc including GET and POST.
It is some times necessary to disable this functionality especially when handling multipart requests. In order to disable it at a specific request (regarding mulripart), I was using the following code.
package resolver;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartResolver;
/**
* Disables the spring multipart resolver for specific client requests and
* therefore keeps the request intact so that controllers can process it in
* whatever way they wish. This behaviour is triggered by a particular GET
* parameter in the client request so it is configurable.
* #see MultipartResolver
*/
#Aspect
public final class MultipartResolverDisablingAspect
{
/**
* GET parameter which, if present in request, enables advice.
*/
private static final String DISABLING_HTTP_REQUEST_PARAMETER_KEY = "_multipartResolverDisable";
private static boolean disablingParameterExists(final HttpServletRequest request)
{
Assert.notNull(request);
return request.getParameter(DISABLING_HTTP_REQUEST_PARAMETER_KEY) != null;
}
/**
* If above GET parameter exists in request then prompt the spring multipart
* resolver to always tell spring that request is not of type multipart.
* Spring then does not process the request any further.
* #param pjp
* #param request
* #return
* #throws Throwable
*/
#Around("isMultipartOperation() && args(request)")
public Object disableIsMultipartOperation(final ProceedingJoinPoint pjp, final HttpServletRequest request) throws Throwable
{
Assert.notNull(pjp);
Assert.notNull(request);
if (disablingParameterExists(request))
{
return Boolean.FALSE;
}
return pjp.proceed();
}
/**
* Applies to any implementation of {#linkplain MultipartResolver}
*/
#SuppressWarnings("unused")
#Pointcut("execution(public boolean " + "org.springframework.web.multipart.MultipartResolver." + "isMultipart(javax.servlet.http.HttpServletRequest))")
private void isMultipartOperation() {}
}
and in the application-context.xml file, the following xml is required.
<aop:aspectj-autoproxy proxy-target-class="false" />
<bean class="resolver.MultipartResolverDisablingAspect" /> <!--Registers the above bean (class)-->
This code was taken from this article under the section - MULTIPART RESOLVER DISABLING ASPECT.
It is meant to disable multipart processing by HiddenHttpMethodFilter when a GET parameter multipartResolverDisable=1 is used as a query string as specified by the code above so that one can use commons fileupload as usual (when multipartResolverDisable=1 is supplied as a query string)
The actual question is still not in the picture. This approach was earlier working correctly in the following environment (with NetBeans 6.9.1).
Spring 3.2.0
Apache Tomcat 6.0.26.6 with the Servlet API 2.5.
Recently I have upgraded NetBeans 7.2.1 with Apache Tomcat 7.0.35 which has the Servlet API 3.0. The Spring version is the same as it was before - Spring 3.2.0.
With this updates, the approach as described above caused the following exception.
Caused by: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name
'org.springframework.transaction.config.internalTransactionAdvisor':
Cannot resolve reference to bean
'org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0'
while setting bean property 'transactionAttributeSource'; nested
exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name
'org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0':
Initialization of bean failed; nested exception is
**java.lang.IllegalArgumentException: error at ::0 can't find referenced
pointcut isMultipartOperation**
Where the isMultipartOperation() is the last method in the above class.
There might be a small change to the code but I know a very little about AOP and cannot figure out the cause of this exception in this nice-looking code.
What is the cause of this exception? Dose it have to do something with the Servlet API?
I think you should change your around advice to
#Around("isMultipartOperation(..) && args(request)")
your pointcut annotation to
#Pointcut("execution(* org.springframework.web.multipart.MultipartResolver.isMultipart(..)) && args(request)")
and the pointcut annotated method to
private void isMultipartOperation(HttpServletRequest request) {}
From memory, I had this issue where I was trying to wrap a pointcut with some advice, and the advice had a different number of arguments to the pointcut. In your code you appear to be using #Around to target a pointcut with args(request), but your pointcut method has no such parameter.

Resources