I am writing a SoapHandler to intercept SOAP calls. I'm testing whether its an incoming or outgoing soap message as follows:
public class MySoapHandler implements SOAPHandler<SOAPMessageContext> {
private static Logger logger = Logger.getLogger(MySoapHandler.class);
#Override
public boolean handleMessage(SOAPMessageContext context) {
Boolean isRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if(isRequest)logger.debug("isRequest=true");
else logger.debug("isRequest=false");
When I submit the soap request I see "isRequest=true" occur twice, first when the soap request is received, and then again just before the soap response is sent. I was expecting to see "isRequest=false" the second time.
Can anyone shed some light?
Boolean needs to be boolean:
public class MySoapHandler implements SOAPHandler<SOAPMessageContext> {
private static Logger logger = Logger.getLogger(MySoapHandler.class);
#Override
public boolean handleMessage(SOAPMessageContext context) {
boolean isRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if(isRequest)logger.debug("isRequest=true");
else logger.debug("isRequest=false");
Related
From what I understand about mocking, the test should not go deeper into the bean being mocked. For example the control flow shouldn't go into the function apiService.getSomeData() and instead it should just return the string "Hello there".
But is that how mocking works or does the program keep going deeper and should I be able to see the print statements of getSomeData() in the stdout?
When I actually run the code below, it doesn't go deeper. But is that how it's supposed to work?
Suppose this is the Rest Controller Code:
#RestController
#RequestMapping(value = "/testing")
public class ApiController {
#Autowired
ApiService service;
#PostMapping(path = "/events/notifications",consumes = "application/json", produces = "application/json" )
public ResponseEntity<String> checkMapping(#Valid #RequestBody String someData, #RequestHeader(value="X-User-Context") String xUserContext) throws Exception {
String response = service.getSomeData(someData);
return ResponseEntity.status(HttpStatus.OK).body(response);
}
}
Suppose this is the Controller test code:
#WebMvcTest(ApiController.class)
public class ApiControllerTest {
#Autowired
MockMvc mockMvc;
#Autowired
ObjectMapper mapper;
#MockBean
ApiService apiService;
#Test
public void testingApi() throws Exception {
Mockito.when(apiService.getSomeData("")).thenReturn("Hello there");
MockHttpServletRequestBuilder mockRequest = MockMvcRequestBuilders.post("/testing/events/notifications")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.header("X-User-Context","something")
.content("something");
mockMvc.perform(mockRequest)
.andExpect(status().isBadGateway());
}
}
Suppose this is the Api Service code:
#Service
public class ApiServiceImpl implements ApiService{
#Override
public String getSomeData(String data) throws Exception {
System.out.println("Going deeper in the program flow);
callThisFunction();
return "Some data";
}
public void callThisFunction(){
System.out.println("Going two levels deeper");
}
}
In your test you are not talking to ApiServiceImpl at all, but an instance that is created by mockito and that is also implementing the ApiService interface. Therefore, your implementation of getSomeData() is not executed at all. That's what mocking is about. You create a "mock" implementation (or let a tool like mockito do it for you) of the thing you do not want to be executed and inject it instead of the "real" thing.
In my controller, i am doing a validation check in if block using a validator class.
Since validation code is not complete yet i just want to test that if
validation was successful the response should be 201(Success).
For this i am mocking the behaviour of validator object to return true
i.e its valid.
The problem is even if i have mocked the validator object behaviour to not enter if block so it doesn't throw validation Exception, its actually entering and throwing exception. That means mocking hasn't worked. I know its bad to assume the method implementation for testing, but i want to know why its not working.
myController.class
#RestController
#RequestMapping("/v1/api")
public class myController {
#Autowired
private RequestService requestService;
#Autowired
RequestValidator validator;
#PostMapping(value = "")
#ResponseStatus(code = HttpStatus.CREATED)
public #ResponseBody
BaseResponse<Response> create(#RequestBody Request<Data> request){
if(!validator.isValidRequestObject(request))
{
throw new RuntimeException("invalid request");
}
Response data = requestService.submitData(request);
BaseResponse<Response> response = new BaseResponse<Response>();
response.setData(data);
response.setMessage("Created Successfully");
return response;
}
ControllerTest.class
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class ControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
Request<Data> request;
#MockBean
RequestService service;
#MockBean
RequestValidator validator;
ObjectMapper mapper = new ObjectMapper();
#Test
public void createTest() throws Exception {
when(validator.isValidRequestObject(request)).thenReturn(true);
when(service.submitData(request)).thenReturn(null);
this.mockMvc.perform(post("/v1/api")
.content(mapper.writeValueAsString(request))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isCreated());
}
If i remove the throw new exception line, test passes.
i assume that since i have mocked validator behaviour in myController
that should not enter if block and hence not throw Exception.
But its entering the if block and throwing exception defying the mocked behaviour.
I'm trying to write a Unit Test for camel to check if an exchange property was correctly set based on the response of the external mocked service. However I can get the property after the call as I can't access the original exchange after mocked external service has been called.
public class OutputTest extends CamelBlueprintTestSupport {
#Override
protected String getBlueprintDescriptor() {
return "/OSGI-INF/blueprint/blueprint-camel.xml,/OSGI-INF/blueprint/blueprint-beans.xml";
}
#Override
public boolean isUseAdviceWith() {
return true;
}
#Override
public String isMockEndpointsAndSkip() {
return "wmq:.*|jetty:.*";
}
#Test
public void testCallAndPropertyIsSet() throws Exception {
getMockEndpoint("mock:jetty:http:localhost").expectedBodiesReceived(context.getTypeConverter().convertTo(String.class, new File("src/test/resources/requests/Request.xml")));
getMockEndpoint("mock:jetty:http:localhost").returnReplyBody(new Expression() {
#Override
public <T> T evaluate(Exchange exchange, Class<T> aClass) {
return context.getTypeConverter().convertTo(aClass, new File("src/test/resources/requests/Response.xml"));
}
});
template.sendBody("direct:route1", context.getTypeConverter().convertTo(String.class, new File("src/test/resources/requests/ValidRequest.xml")));
getMockEndpoint("mock:jetty:http:localhost").expectedBodiesReceived();
//How to assert exchange property 'Property1' has been set?
}
Blueprint camel route:
<route id="rav">
<from uri="direct:route1"/>
<to uri="velocity:templates/RequestTemplate.vm"/>
<to uri="jetty:{{integration.service.service1}}?bridgeEndpoint=true"/>
<setProperty propertyName="Property1">
<xpath resultType="java.lang.String">/soapenv:Envelope/soapenv:Body/namespace:element/text()</xpath>
</setProperty>
</route>
There are different ways to do this. You can try to use the MockEndpoint and get the exchange from there. Another approach is:
Exchange exchange = template.send("uri", new Processor() {
public void process(Exchange exchange) throws Exception {
exchange.getIn().setBody("");
}
});
Message resp = exchange.getIn();
assertEquals("someproperty", resp.getProperty("propertyName"));
I have a class that starts a server:
public class SocketServer
{
private static IXSocketServerContainer server = null;
public SocketServer()
{
server = XSockets.Plugin.Framework.Composable
.GetExport<IXSocketServerContainer>();
}
public bool StartServers()
{
try
{
server.StartServers();
return true;
} catch
{
return false;
}
}
this class has a method:
public void SendEventMessageToAllClients(string message)
{
XSockets.Core.XSocket.Helpers.XSocketHelper
.SendToAll<MyController>(new MyController(), message, "events");
}
where MyController is my own controller, it is implemented and the server can find it and this method work.
Now I would like to expand the functionality with a new method that allows me to send an event to an specific client:
public void SendEventMessageToClient(string clientId, string message)
{
XSockets.Core.XSocket.Helpers.XSocketHelper
.SendTo<MyController>(new MyController(),
p => p.ClientId == clientId, message, "events");
}
Is this the right approach or am I doing something wrong?
Thanks!
I would not recomend that approach, I have not even tested if that actaully works.
You create a new controller every time just to be able to access the extension method.
I am guessing that since you have this on the class starting the server you only use this as a publisher?
If so the correct way would be to install the XSockets.Client package and use the client pool to publish messages: client pool documentation
Example with client pool
The nice thing about the client pool is that you do not need to create an instance every time. The pool will reuse your connection to the controller.
Using the clientpool (or a real client connection) will ensure that the message pass through the Pipeline and all interceptors if you have any. Using a controller instance directly will never reach the pipline, interceptors etc.
//Get a pool client
ClientPool poolClient =
XSockets.Client.ClientPool.GetInstance("ws://127.0.0.1:4502/MyController", "*");
Methods for sending a message to the controller.
public void SendEventMessageToClient(Guid clientId, string message)
{
poolClient.Send(new {clientId, message}, "SendEventMessageToClient");
}
public void SendEventMessageToAllClients(string message)
{
poolClient.Send(message, "SendEventMessageToAllClients");
}
The controller
public void SendEventMessageToClient(Guid clientId, string message)
{
this.SendTo(p => p.ClientId == clientId, message, "SendEventMessageToClient");
}
public void SendEventMessageToAllClients(string message)
{
this.SendToAll(message, "SendEventMessageToAllClients");
}
Example with instance of controller
If you decide to use the way you have done you should at least create on ONE instance of the controller to use the in the server class.
Important: Using a controller instance directly will never reach the pipline, interceptors etc.
//Be aware of the fact that this controller NEVER will have a connection.
//It can only send to others, it can never receive messages!
MyController c = new MyController();
//You should probably have a Guid here instead of string
//Also note that the client have to subscribe for "events" to get the message
public void SendEventMessageToClient(Guid clientId, string message)
{
this.SendTo(p => p.ClientId == clientId, message, "SendEventMessageToClient");
}
public void SendEventMessageToAllClients(string message)
{
this.SendToAll(message, "SendEventMessageToAllClients");
}
Since I do not know what you are trying to accomplish Im not sure this is the best way, but one of the merhods above should work.
EDIT: Also, in a real application you probably dont have access to the MyController class since it probably is in a separate assembly not being referenced at compile time. So then you approach will not even be possible and the way to go then is client or clientpool
/Uffe
Uffe, you're right an the ClientPool is the right option for me, I had problems running your code because some of the mappings proposed by you are not working, here is your proposed solution slightly modified to make it run:
//Get a pool client
ClientPool poolClient = XSockets.Client.ClientPool.GetInstance("ws://127.0.0.1:4502/MyController", "*");
Methods for sending a message to the controller. ITextArgs are needed in this case
public void SendEventMessageToClient(Guid clientId, string message)
{
ITextArgs textargs = new TextArgs(mess, "SendEventMessageToClient");
poolClient.Send(new {clientId = guid, message = "Hello to one client"}, "SendEventMessageToClient");
}
Here, I TextArgs is not needed, it can be used, but string works also properly. It seems that the conversion to ITextArgs works fine here.
public void SendEventMessageToAllClients(string message)
{
poolClient.Send("hello all", "SendEventMessageToAllClients");
}
The controller: Only ITextArgs messages are mapped. Using string will not work.
public void SendEventMessageToClient(Guid clientId, ITextArgs message)
{
c.SendTo(p => p.ClientId == clientId, message.data, "events");
}
public void SendEventMessageToAllClients(ITextArgs message)
{
c.SendToAll(message.data, "events");
}
Thanks you very much Uffe for your help!
OK, we're talking Spring (3.2.0) MVC
We have an pointcut defined to be triggered "around" an annotation like so:
#Around("#annotation(MyAnnotation)")
public void someFunction() {
}
Then in a controller we have:
#Controller
#Component
#RequestMapping("/somepath")
public class MyController {
#Autowired
private MyService service;
...
#MyAnnotation
#RequestMapping(value = "/myendpoint", method = RequestMethod.POST, produces = "application/json")
#ResponseBody
public Object myEndpoint(#RequestBody MyRequestObject requestObject, HttpServletRequest request, HttpServletResponse response) {
...
return service.doSomething(requestObject);
}
}
Then we have a unit test that looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = {"../path/to/applicationContext.xml"})
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
public class MyControllerTest {
private MockMvc mockMvc;
#InjectMocks
private MyController controller;
#Mock
private MyService myService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void myTest() {
MyRequest request = new MyRequest();
MyResponse response = new MyResponse();
String expectedValue = "foobar";
Mockito.when(myService.doSomething((MyRequest) Mockito.any())).thenReturn(response);
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post("/myendpoint");
String request = IOUtils.toString(context.getResource("classpath:/request.json").getURI());
builder.content(request);
builder.contentType(MediaType.APPLICATION_JSON);
mockMvc.perform(builder)
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.someKey").value(expectedValue));
Mockito.verify(myService, Mockito.times(1)).doSomething((MyRequest) Mockito.any());
}
}
The test runs fine, but the aspect defined around the annotation (MyAnnotation) does not execute. This executes just fine when the endpoint is triggered by a real request (e.g. when running in a servlet container) but just doesn't fire when running in the test.
Is this a particular "feature" of MockMvc that it doesn't trigger aspects?
FYI our applicationContext.xml is configured with:
<aop:aspectj-autoproxy/>
and as I mentioned the aspects do actually work in reality, just not in the test.
Anyone know how to get these aspects to fire?
Thanks!
OK.. so the solution eventually presented itself from.. you guessed it.. reading the documentation :/
http://docs.spring.io/spring-framework/docs/3.2.0.BUILD-SNAPSHOT/reference/htmlsingle/#spring-mvc-test-framework
Furthermore, you can inject mock services into controllers through Spring configuration, in order to remain focused on testing the web layer.
So the final solution looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = {"testContext.xml","../path/to/applicationContext.xml"})
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
public class MyControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext wac;
#Autowired
private MyService myService;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
#Test
public void myTest() {
MyRequest request = new MyRequest();
MyResponse response = new MyResponse();
String expectedValue = "foobar";
Mockito.when(myService.doSomething((MyRequest) Mockito.any())).thenReturn(response);
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post("/myendpoint");
String request = IOUtils.toString(context.getResource("classpath:/request.json").getURI());
builder.content(request);
builder.contentType(MediaType.APPLICATION_JSON);
mockMvc.perform(builder)
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.someKey").value(expectedValue));
Mockito.verify(myService, Mockito.times(1)).doSomething((MyRequest) Mockito.any());
}
}
Then you simply define a context file for this test testContext.xml that has the mock of the service object:
<bean id="myService" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.mypackage.MyService"/>
</bean>
Importantly the MyService instance is #Autowired into the test so it can be orchestrated.
This allows you to mock out any instances you like, whether they are in service classes, aspects etc as long as you name the bean appropriately. So in this case the MyService would be declared as:
#Component("myService")
public class MyService {
...
you need to enable aop in test:
#EnableAspectJAutoProxy
#RunWith(SpringJUnit4ClassRunner.class)
public class MyControllerTest {
}
I had a similar setup using MockMVC to perform integration tests through the layers. The Annotation was working fine without any extra code until I renamed the annotation's package. It stopped working. The annotation would not execute. After banging my head for a while, I realized I had already solved this once: ComponentScan the annotation's package.
You probably have a similar statement in one of you application's configuration class files. You will need to repeat any of those types of declarations here in your Test Class.
#SpringBootTest
#AutoConfigureMockMvc
#ComponentScan("annotation.package")
public class MyControllerTest {
}