Spring boot 1.4 Integration tests pass but Application start fails - spring-mvc

We just upgraded to Spring Boot 1.4 and are using the Spring Boot testing improvements. We have integration tests of the form:
RunWith(SpringRunner.class)
#SpringBootTest(classes = {
ApplicationConfiguration.class }, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ServiceIntegrationTest {
private static final String AEndpoint = Constants.A_REST_CONTROLLER_ENDPOINT;
private static final String TEndpoint = Constants.T_REST_CONTROLLER_ENDPOINT;
#Autowired
private TestRestTemplate testRestTemplate;
public void postJsonNode(String postUrl, JsonNode requestBody) {
HttpEntity<JsonNode> request = new HttpEntity<JsonNode>(requestBody, this.headers);
ResponseEntity<String> post = this.testRestTemplate.postForEntity(postUrl, request, String.class);
assertThat(post.getStatusCode(), equalTo(HttpStatus.CREATED));
}
These kinds of tests pass but the Application fails to start due to ObjectMapper getting deprecated and replaced by a pre-wired _halMapper (I referred to as bean wiring issue).
***************************
APPLICATION FAILED TO START
***************************
Description:
Constructor in org.springframework.boot.actuate.autoconfigure.EndpointMBeanExportAutoConfiguration required a single bean, but 2 were found:
- objectMapper: defined by method 'objectMapper' in com.service.ApplicationConfiguration
- _halObjectMapper: defined in null
Application class:
public class Application {
public static void main(String[] args) {
SpringApplication.run(ApplicationConfiguration.class, args);
}
}
ApplicationConfiguration class:
#SpringBootApplication(scanBasePackages = "com.service")
public class ApplicationConfiguration {
#Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
return objectMapper;
}
}
Is there a way to test the Application start through integration tests?

Related

RedisSentinelConfiguration using spring.redis.sentinel.nodes with spring boot

Trying to configure spring boot application with spring-session and redis but having below issue. Not able to resolve it.
Constructor threw exception; nested exception is java.lang.IllegalStateException: BeanFactory has not been injected into #Configuration class
This code works fine for me
#Configuration
#EnableRedisHttpSession
public class HttpSessionConfig {
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.readFrom(SLAVE_PREFERRED)
.build();
RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
.master("mymaster")
.sentinel("192.168.56.50", 26379)
.sentinel("192.168.56.50", 26380)
.sentinel("192.168.56.50", 26381);
#Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory(sentinelConfig, clientConfig);
}
}
but not this code using PropertySource.
Spring document says:-
**RedisSentinelConfiguration can also be defined with a PropertySource, which lets you set the following properties:
Configuration Properties
spring.redis.sentinel.master: name of the master node.
spring.redis.sentinel.nodes: Comma delimited list of host:port pairs.**
#Configuration
#EnableRedisHttpSession
#PropertySource(name="application", value="classpath:application.properties")
public class HttpSessionConfig {
#Resource
ConfigurableEnvironment environment;
#Bean
public PropertiesPropertySource propertySource() {
return (PropertiesPropertySource) environment.getPropertySources().get("defaultProperties");
}
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.readFrom(SLAVE_PREFERRED)
.build();
RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration(propertySource());
#Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory(sentinelConfig, clientConfig);
}
}
application.properties
server.port=8090
spring.security.user.name=admin
spring.security.user.password=admin
spring.redis.sentinel.master=mymaster
spring.redis.sentinel.nodes=192.168.56.50:26379,192.168.56.50:26380,192.168.56.50:26381
spring.application.name=spring-session-demo
The format of sentinel nodes property is comma separated key:value pairs. So you can extract host and port by java split() function.
#Autowired
private Environment env;
#Bean
public LettuceConnectionFactory connectionFactory() {
RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration();
String master = env.getProperty("spring.redis.sentinel.master");
String nodes = env.getProperty("spring.redis.sentinel.nodes");
sentinelConfig.master(master);
for (String node : nodes.split(",")) {
String split[] = node.split(":");
sentinelConfig.sentinel(split[0], Integer.parseInt(split[1]));
}
...
}

No mapping found for HTTP request with URI [/api/transactions] in DispatcherServlet with name ''

I thought this is a standard configuration. But I get a 404 back. Where else should I configure Spring Boot ?
#RestController
#RequestMapping("/api")
public class TransactionStatisticsController {
public static final Logger logger = LoggerFactory.getLogger(TransactionStatisticsController.class);
#RequestMapping(value = "/transactions",
method = RequestMethod.POST)
public ResponseEntity sendTransaction(#RequestBody Transaction request) {
logger.info( request.toString());
return new ResponseEntity(HttpStatus.OK);
}
}
This is my test.
#JsonTest
#SpringBootTest(classes = Application.class)
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
public class TransactionStatisticsRestTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private JacksonTester<Transaction> json;
private static Transaction transaction;
#BeforeClass
public static void createTransaction(){
BigDecimal amount = new BigDecimal(12.3343);
transaction = new Transaction(amount.toString(),
"2010-10-02T12:23:23Z");
}
#Test
public void getTransactionStatus() throws Exception {
final String transactionJson = json.write(transaction).getJson();
mockMvc
.perform(post("/api/transactions")
.content(transactionJson)
.contentType(APPLICATION_JSON_UTF8))
.andExpect(status().isOk());
}
public static byte[] convertObjectToJsonBytes(Object object) throws IOException {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsBytes(transaction);
}
}
Request being made is
MockHttpServletRequest:
HTTP Method = POST
Request URI = /api/transactions
Parameters = {}
Headers = {Content-Type=[application/json;charset=UTF-8]}
Body = {"amount":"12.3343000000000007077005648170597851276397705078125","timestamp":"2010-10-02T12:23:23Z[UTC]"}
Session Attrs = {}
Handler:
Type = null
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 404
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Update : I added a component scan pointing to a base package. I don't see that error now. Please see the comments where there is an answer.
As in the comment section ,there was only requirement was to bind a component scan base package location .
#Component scan -->Configures component scanning directives for use with #Configuration classes. Provides support parallel with Spring XML's element.
Either basePackageClasses() or basePackages() (or its alias value()) may be specified to define specific packages to scan. If specific packages are not defined, scanning will occur from the package of the class that declares this annotation.
Please share your project folder architecture. It might be possible that your controller package is out of the main class package. That's why it is showing 404.
This code :
#RestController
#RequestMapping("/api")
public class TransactionStatisticsController {
public static final Logger logger = LoggerFactory.getLogger(TransactionStatisticsController.class);
#RequestMapping(value = "/transactions",
method = RequestMethod.POST)
public ResponseEntity sendTransaction(#RequestBody Transaction request) {
logger.info( request.toString());
return new ResponseEntity(HttpStatus.OK);
}
}
This should be into your main package where
#SpringBootApplication
public class YourApplication {
public static void main(String[] args) {
SpringApplication.run(YourApplication.class, args);
}
}
this main class resides.
I hope, this will help.
Seems using #JsonTest does not even allow to load Application Context, results mapping is not loaded and its throw 404 so #JsonTest is not a replacement for #SpringBootTest, it is a way to easily test json serialization/de-serialization.
As per documentation:
you can use the #JsonTest annotation. #JsonTest auto-configures the
available supported JSON mapper, which can be one of the following
libraries:
Jackson ObjectMapper, any #JsonComponent beans and any Jackson Modules
Gson
Jsonb
If by using Gson and removing #JsonTest your test run fine..(add Gson Dependency in pom)
#SpringBootTest
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
public class DemoKj01ApplicationTests {
#Autowired
private MockMvc mockMvc;
private static Transaction transaction;
#BeforeClass
public static void createTransaction(){
BigDecimal amount = new BigDecimal(12.3343);
transaction = new Transaction(amount.toString(),
"2010-10-02T12:23:23Z");
}
#Test
public void getTransactionStatus() throws Exception {
//final String transactionJson = json.write(transaction).getJson();
Gson gson = new Gson();
String jsonRequest = gson.toJson(transaction);
mockMvc
.perform(post("/api/transactions")
.content(jsonRequest)
.contentType(APPLICATION_JSON_UTF8))
.andExpect(status().isOk());
}
It is beacause of the trailing slas in #RequestMapping(value = "/transactions/", method = RequestMethod.POST)
Remove it and it will be ok : value = "/transactions/" => value = "/transactions"

CordaApp with Spring Boot Api different response

I have integrated the cordapp with spring boot .One strange observation we found that the response differs coming from the cordawebserver and spring boot server for example
Blockquote
API :GET: market/me
gives
{
“me”: {
“commonName”: null,
“organisationUnit”: null,
“organisation”: “PartyG-CT”,
“locality”: “Tokyo”,
“state”: null,
“country”: “JP”,
“x500Principal”: {
“name”: “O=PartyG-CT,L=Tokyo,C=JP”,
“encoded”: “MDExCzAJBgNVBAYTAkpQMQ4wDAYDVQQHDAVUb2t5bzESMBAGA1UECgwJUGFydHlHLUNU”
}
}
}
with Spring boot
while with cordawebserver we are getting :
{
“me”: “C=JP,L=Tokyo,O=PartyG-CT”
}
Same behaviour we are finding the same for different APIs Any help will be appreciated
#GET
#Path("peers")
#Produces(javax.ws.rs.core.MediaType.APPLICATION_JSON)
// CordaX500Name
public Map<String, List<CordaX500Name>> getPeers() {
List<NodeInfo> nodeInfoSnapshot = proxy.networkMapSnapshot();
return ImmutableMap.of(
"peers",
nodeInfoSnapshot
.stream()
.map(node -> node.getLegalIdentities().get(0).getName())
.filter(name -> !name.equals(myLegalName) && !name.getOrganisation().equals(controllerName)
&& !name.getOrganisation().equals(NETWORK_MAP_NAME))
.collect(toList()));
}
//boot entry point
#SpringBootApplication
public class FacilityServer {
#Autowired
public static NodeRPCConnection nodeRPCConnection;
/**
* Starts our Spring Boot application.
*/
public static void main(String[] args) throws Exception {
SpringApplication springApplication = new SpringApplication();
springApplication.setBannerMode(Banner.Mode.OFF);
springApplication.run(FacilityServer.class, args);
}
#EventListener(ApplicationReadyEvent.class)
public void initiateFacilityObserverPostStartup() throws Exception{
FacilityObserver.startFacilityWatch();
}
// this class for using the jersey instead of spring rest impltn
#Configuration
#ApplicationPath("rest")
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
}
#PostConstruct
public void setUp() {
register(MarketApi.class);
//register(GenericExceptionMapper.class);
}
}
I found the solution ;According to the corda documentation we need to register cordajackson mapper .Then, we will be able to get the same response as in corda webserer.
for example:
List<StateAndRef<YourState>> vaultStatesList = vaultStates.getStates();
ObjectMapper mapper = JacksonSupport.createNonRpcMapper();
String json = mapper.writeValueAsString(vaultStatesList);

How to write a mockito test case for ResourceAssembler with in Spring Hateos?

I am trying to write a unit test for the below Assembler but i keep getting Could not find current request via RequestContextHolder. Is this being called from a Spring MVC handler?. I wanted to know how i can mock out the resource creation?
#Component
public class LoginResourceAssembler extends ResourceAssemblerSupport<User, ResourceSupport> {
public LoginResourceAssembler() {
super(User.class, ResourceSupport.class);
}
#Override
public ResourceSupport toResource(User user) {
ResourceSupport resource = new ResourceSupport();
final String id = user.getId();
resource.add(linkTo(MyAccountsController.class).slash(id).slash("accounts").withRel("accounts"));
return resource;
}
}
Instead of changing from a plain unit test to a IMO integration test (given dependency of the spring framework) you could do something like:
#RunWith(MockitoJUnitRunner.class)
public class LoginResourceAssemblerTest {
#InjectMocks
private LoginResourceAssembler loginResourceAssembler;
#Before
public void setup() {
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(new MockHttpServletRequest()));
}
#Test
public void testToResource() {
//...
}
}
I was seeing the error Could not find current request via RequestContextHolder. Is this being called from a Spring MVC handler because my test class was annotated with #RunWith(MockitoJUnitRunner.class) and this was not injecting the controller.
To fix this error, i annotated my test case with
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
A working test case in my case
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class LoginResourceAssemblerTest {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#InjectMocks
private LoginResourceAssembler loginResourceAssembler;
#Before
public void setUp() {
initMocks(this);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
#Test
public void testToResource() {
User user = new User();
user.setId("1234");
ResourceSupport resource = loginResourceAssembler.toResource(user);
assertEquals(1,resource.getLinks().size());
assertEquals("accounts",resource.getLinks().get(0).getRel());
assertTrue(resource.getLinks().get(0).getHref().contains("accounts"));
}
}

Apache Camel Integration test results in OutOfMemory

I have a simple Camel route that sends data to an external REST interface, integrated with Spring MVC.
#RestController
public class MyController {
#Autowired
private camelService camelService;
#RequestMapping(method = RequestMethod.POST, value = "/test")
#ResponseStatus(HttpStatus.CREATED)
public TestModel createEV(#Valid #RequestBody TestModel testModel) {
camelService.publishTestCase(testModel);
return testModel;
}
}
#Service
public class CamelService {
#Produce(uri = "direct:test")
private ProducerTemplate producerTemplate;
#Override
public void publishTestCase(TestModel testModel) {
producerTemplate.sendBody(testModel);
}
}
#Component
public class TestRouter extends SpringRouteBuilder
#Override
public void configure() throws Exception {
errorHandler(loggingErrorHandler(log).level(LoggingLevel.ERROR));
from("direct:test")
.routeId("testRoute")
.beanRef("mapper", "map")
.to("velocity:test.vm")
.setHeader(Exchange.HTTP_METHOD, simple("POST"))
.to("log:test?level=DEBUG&showAll=true&multiline=true&maxChars=100000")
.to("cxfrs:http://url.here");
}
}
Then there's an integration test for the rest endpoint with mockMvc that mocks the external camel endpoint.
#ContextConfiguration(loader = SpringApplicationContextLoader, classes = Application)
#WebIntegrationTest
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
class MyControllerIntegrationTest extends Specification {
#Autowired
private CamelContext camelContext
MockMvc mockMvc
#Autowired
WebApplicationContext wac
#Shared
def setupOnceRun = false
#Shared
def validInput = new File(JSON_DIR + '/valid.json').text
def setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build()
// no way to use setupSpec as Autowired fields can't be Shared
if (!setupOnceRun) {
ModelCamelContext mcc = camelContext.adapt(ModelCamelContext);
camelContext.getRouteDefinition("testRoute).adviceWith(mcc, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
mockEndpointsAndSkip("cxfrs:http://url.here")
}
});
setupOnceRun = true
}
}
def 'valid scenario'() {
setup:
MockEndpoint mockEndpoint = (MockEndpoint) camelContext.hasEndpoint("mock:cxfrs:http://url.here")
mockEndpoint.expectedMessageCount(1)
when:
def response = mockMvc.perform(post('/test').content(validInput)).andReturn()
then:
response.getResponse().getStatus() == 201
mockEndpoint.assertIsSatisfied()
}
}
The test is passing if you run it by itself but once it's included in the build together with other integration tests it's constantly producing OutOfMemory. MaxPermSize is 4G, it doesn't look like that's an issue. I am new to Camel so my guess is that I am wiring the test in a wrong way. Would appreciate any suggestions.

Resources