Subresource for target class has no jax-rs annotations - web-services

I am trying to call a webservice method via a proxy but I have got an error message that says: "Subresource for target class has no jax-rs annotations.: org.jboss.resteasy.core.ServerResponse"
Here is my server class
#Path("/authorizationCheck")
public class AuthorizationRestService implements AuthorizationService {
#Override
#Path("/webserviceTest")
public Response webserviceTest(){
TestDTO x = new TestDTO();
x.setFieldOne("ffff");
x.setFieldTwo("gggg");
Response res = Response.ok(x).build();
return res;
}
}
with a an interface like this
#Path("/authorizationCheck")
public interface AuthorizationService {
#POST
#Path("/webserviceTest")
public Response webserviceTest();
}
and my return object wrapped in response
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class TestDTO {
private String fieldOne;
private String fieldTwo;
public String getFieldOne() {
return fieldOne;
}
public void setFieldOne(String fieldOne) {
this.fieldOne = fieldOne;
}
public String getFieldTwo() {
return fieldTwo;
}
public void setFieldTwo(String fieldTwo) {
this.fieldTwo = fieldTwo;
}
}
and finally my client class
#Stateful
#Scope(ScopeType.CONVERSATION)
#Name("authorizationCheckService")
public class AuthorizationCheckService {
public void testWebservice(){
RegisterBuiltin.register(ResteasyProviderFactory.getInstance());
AuthorizationService proxy =
ProxyFactory.create(AuthorizationService.class,
ApplicationConfig.WORKFLOWSERVER_URL + "services/authorizationCheck/webserviceTest");
Response response = proxy.webserviceTest();
return;
}
}
what I am doing wrong here , any help will be appreciated.

You have two annotations with webserviceTest() which are #POST and #Path.
Repeat BOTH the annotations in over ridden method in implemented class. That means add the #POST annotation to webserviceTest() method.
It should work then !
And here is the reason why it din't work.. without proper annotations in implementing class.
Why java classes do not inherit annotations from implemented interfaces?

You can remove the #Path annotations on the implementation class and concrete method, and only annotate your interfaces, like this:
public class AuthorizationRestService implements AuthorizationService {
#Override
public Response webserviceTest(){
TestDTO x = new TestDTO();
x.setFieldOne("ffff");
x.setFieldTwo("gggg");
Response res = Response.ok(x).build();
return res;
}
}
Note: don't forget #Produces on your interface method to define your MIME type, such as MediaType.APPLICATION_XML
#Path("/authorizationCheck")
public interface AuthorizationService {
#POST
#Path("/webserviceTest")
#Produces(MediaType.APPLICATION_XML)
public Response webserviceTest();
}
See an example here: http://pic.dhe.ibm.com/infocenter/initiate/v9r5/index.jsp?topic=%2Fcom.ibm.composer.doc%2Ftopics%2Fr_composer_extending_services_creating_rest_service_rest_interface.html
and here: http://pic.dhe.ibm.com/infocenter/initiate/v9r5/index.jsp?topic=%2Fcom.ibm.composer.doc%2Ftopics%2Fr_composer_extending_services_creating_rest_service_rest_interface.html

I changed like this
#Path("/authorizationCheck")
public class AuthorizationRestService implements AuthorizationService {
#Override
#Path("/webserviceTest")
#POST
#Produces(MediaType.APPLICATION_XML)
public Response webserviceTest(){
TestDTO x = new TestDTO();
x.setFieldOne("ffff");
x.setFieldTwo("gggg");
Response res = Response.ok(x).build();
return res;
}
}
My Test Client is different
public class CustomerResourceTest
{
#Test
public void testCustomerResource() throws Exception
{
URL postUrl = new URL("http://localhost:9095/authorizationCheck/webserviceTest");
HttpURLConnection connection = (HttpURLConnection) postUrl.openConnection();
connection.setRequestMethod("POST");
System.out.println("Content-Type: " + connection.getContentType());
BufferedReader reader = new BufferedReader(new
InputStreamReader(connection.getInputStream()));
String line = reader.readLine();
while (line != null)
{
System.out.println(line);
line = reader.readLine();
}
Assert.assertEquals(HttpURLConnection.HTTP_OK, connection.getResponseCode());
connection.disconnect();
return;
}
}
It produced output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><testDTO><fieldOne>ffff</fieldOne><fieldTwo>gggg</fieldTwo></testDTO>
Had to add following dependency also
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>2.2.0.GA</version>
</dependency>
Tried your code.
public void testCustomerResource() throws Exception
{
RegisterBuiltin.register(ResteasyProviderFactory.getInstance());
AuthorizationService proxy =
ProxyFactory.create(AuthorizationService.class,"http://localhost:9095/");
ClientResponse response = (ClientResponse) proxy.webserviceTest();
String str = (String)response.getEntity(String.class);
System.out.println(str);
return;
}
Produced same output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><testDTO><fieldOne>ffff</fieldOne><fieldTwo>gggg</fieldTwo></testDTO>
Note how I created proxy. I had only base url **http://localhost:9095/**. I did not mention resources authorizationCheck/webserviceTest in that. This is different from how you coded.

Related

Spring-Webflux: Handler function unit test is throwing UnsupportedMediaTypeStatusException

I am trying to write Unit test to the handler function, I followed the example from the Spring project. Can someone help me why the following test is throwing UnsupportedMediaTypeStatusException?
Thanks
Handler function
public Mono<ServerResponse> handle(ServerRequest serverRequest) {
log.info("{} Processing create request", serverRequest.exchange().getLogPrefix());
return ok().body(serverRequest.bodyToMono(Person.class).map(p -> p.toBuilder().id(UUID.randomUUID().toString()).build()), Person.class);
}
Test Class
#SpringBootTest
#RunWith(SpringRunner.class)
public class MyHandlerTest {
#Autowired
private MyHandler myHandler;
private ServerResponse.Context context;
#Before
public void createContext() {
HandlerStrategies strategies = HandlerStrategies.withDefaults();
context = new ServerResponse.Context() {
#Override
public List<HttpMessageWriter<?>> messageWriters() {
return strategies.messageWriters();
}
#Override
public List<ViewResolver> viewResolvers() {
return strategies.viewResolvers();
}
};
}
#Test
public void handle() {
Gson gson = new Gson();
MockServerWebExchange exchange = MockServerWebExchange.from(
MockServerHttpRequest.post("/api/create")
.body(gson.toJson(Person.builder().firstName("Jon").lastName("Doe").build())));
MockServerHttpResponse mockResponse = exchange.getResponse();
ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
Mono<ServerResponse> serverResponseMono = myHandler.handle(serverRequest);
Mono<Void> voidMono = serverResponseMono.flatMap(response -> {
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
boolean condition = response instanceof EntityResponse;
assertThat(condition).isTrue();
return response.writeTo(exchange, context);
});
StepVerifier.create(voidMono)
.expectComplete().verify();
StepVerifier.create(mockResponse.getBody())
.consumeNextWith(a -> System.out.println(a))
.expectComplete().verify();
assertThat(mockResponse.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_JSON);
}
}
Error Message:
java.lang.AssertionError: expectation "expectComplete" failed (expected: onComplete(); actual: onError(org.springframework.web.server.UnsupportedMediaTypeStatusException: 415 UNSUPPORTED_MEDIA_TYPE "Content type 'application/octet-stream' not supported for bodyType=com.example.demo.Person"))
I found that I missed .contentType(MediaType.APPLICATION_JSON) to my mock request.
MockServerWebExchange.from(
MockServerHttpRequest.post("/api/create").contentType(MediaType.APPLICATION_JSON)
.body(gson.toJson(Person.builder().firstName("Jon").lastName("Doe").build())));
fixed my issue.

Spring boot How to access Appconfig properties in test case

I am new to web services and spring boot. I have written a service for which I am now writing a test case.
My application gets Soap request, parses the body and saves contents into database.
My test case tests this service.
When I run the application and send a request from Postman, it runs alright. But when I call my service method from test case, I get nullpointer for JaxBcontext.
I have declared Jaxbcontext in my AppConfig.java (which is annotated with #Configuration and my jaxb is a bean with #Bean annotation) in my service, I have #autowire to use jaxbcontext.
I have pasted code snippets for clarity. Please advise me what I am doing wrongly here.
My test case
public class ReferralExchangeEndpointTest {
ReferralExchangeEndpoint referralExchangeEndpoint = new ReferralExchangeEndpoint();
JAXBContext jbcTest;
Marshaller marshaller;
Unmarshaller unmarshaller;
public ReferralExchangeEndpointTest() throws JAXBException {
}
#Before
public void setUp() throws Exception {
jbcTest = JAXBContext.newInstance(
"our app schema"); // this is working fine, I have replaced schema with this text for posting it in stack.
ObjectFactory factory = new ObjectFactory();
marshaller = jbcTest.createMarshaller();
unmarshaller = jbcTest.createUnmarshaller();
}
#Test
public void send() throws Exception {
File payload = new File("payload.xml");
Object x = unmarshaller.unmarshal(payload);
JAXBElement jbe = (JAXBElement) x;
System.out.println(jbe.getName());
Object test = jbe.getValue();
SendRequestMessage sendRequestMessage = (SendRequestMessage) jbe.getValue();
// Method in test.
referralExchangeEndpoint.send(sendRequestMessage);
}
}
My service class
#Endpoint
public class ReferralExchangeEndpoint {
public static final Logger logger = LoggerFactory.getLogger(ReferralExchangeEndpoint.class);
#Autowired
private JAXBContext jaxbContext;
#Autowired
.
.
.
private Form parseBody(String payLoadBody) {
try {
Unmarshaller um = jaxbContext.createUnmarshaller();
return (Form) um.unmarshal(new StringReader(payLoadBody));
} catch (Exception e) {
throw new RuntimeException("Failed to extract the form from the payload body", e);
}
}
My appconfig file
#Configuration
public class AppConfig {
#Bean
public JAXBContext jaxbContext() throws JAXBException {
return
JAXBContext.newInstance("packagename");
}
#Bean public MessagingService messagingService() {
return new MessagingService();
}
}
Thanks.
Kavitha.
** Solved **
My test case now looks like this.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {AppConfig.class})`
public class ReferralExchangeEndpointTest {
#Autowired
ReferralExchangeEndpoint referralExchangeEndpoint;
#Autowired
private JAXBContext jaxbContext;
private Marshaller marshaller;
private Unmarshaller unmarshaller;
#Before
public void setUp() throws Exception {
marshaller = jaxbContext.createMarshaller();
unmarshaller = jaxbContext.createUnmarshaller();
}
#Test
public void send() throws Exception {
File payload = new File("src/test/resources/payload.xml");
JAXBElement jbe = (JAXBElement) unmarshaller.unmarshal(payload);
SendRequestMessage sendRequestMessage = (SendRequestMessage) jbe.getValue();
JAXBElement<SendResponseMessage> response = referralExchangeEndpoint.send(sendRequestMessage);
//TODO add remaining assertions on response after confirming what should the service return for these attributes.
assertEquals("SiteId wrong in response: ", "siteId", response.getValue().getSiteId());
}
}`

WS - Apache CXF: Response.get() in AsyncHandler returns Object instead of message

I am a beginner in WS and CXF. I would like to create asynchronous client using AsyncHandler but I am not able to get response message.
I have web service class and interface generated with CXF wsdl2java like the following:
#WebService(...)
#XmlSeeAlso({ObjectFactory.class})
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface TestInterface {
#WebMethod(operationName = "update")
public Response<?> updateAsync(
#WebParam(partName = "MyMessage", mode = WebParam.Mode.INOUT, name = "MyMessage", targetNamespace = "...")
javax.xml.ws.Holder myMessage
);
#WebMethod(operationName = "update")
public Future<?> updateAsync(
#WebParam(partName = "MyMessage", mode = WebParam.Mode.INOUT, name = "MyMessage", targetNamespace = "...")
javax.xml.ws.Holder myMessage,
#WebParam(name = "asyncHandler", targetNamespace = "")
AsyncHandler<MyMessage> asyncHandler
);
#Action(input = "urn:...", output = "urn:...")
#WebMethod
public void update(
#WebParam(partName = "MyMessage", mode = WebParam.Mode.INOUT, name = "MyMessage", targetNamespace = "...")
javax.xml.ws.Holder<MyMessage> myMessage
);
}
-
#javax.jws.WebService(...)
public class TestInterfaceImpl implements TestInterface {
public Future<?> updateAsync(javax.xml.ws.Holder MyMessage, AsyncHandler<MyMessage> asyncHandler) {
public Response<?> updateAsync(javax.xml.ws.Holder MyMessage) {
return null;
/* not called */
}
public Future<?> updateAsync(javax.xml.ws.Holder MyMessage,AsyncHandler<MyMessage> asyncHandler) {
return null;
/* not called */
}
public void update(javax.xml.ws.Holder<MyMessage> MyMessage) {
LOG.info("Executing operation update");
...
}
}
client class
public class Updater {
#Inject
private RequestInterface requestInterfaceClient;
public void sendRequest(){
Holder<MyMessage> message = createNewMessage();
MyResponseHandler handler = new MyResponseHandler();
RequestInterfaceClient.updateAsync(message, handler);
}
private Holder<MyMessage> createNewMessage() {
...
}
}
And handler
public class MyResponseHandler implements AsyncHandler<MyMessage>{
#Override
public void handleResponse(Response<MyMessage> response) throws Exception {
MyMessage message = response.get();
// ^^^
// returns instance of Object instead of MyMessage
}
}
When response.get() is called java.lang.ClassCastException: java.lang.Object cannot be cast to MyMessage occurs.
When I debug the code, I can see that response contains result which is array of Object. There are 2 objects: 1st is instance of Object (it is returned by result.get()) and 2nd is instance of MyMessage.
My questions follow:
Response has not any getter with index. How can I get the second object?
Why response has those 2 objects and result.get() does not work?
Thank you for any advice.
I prevented CXF from generating methods with javax.xml.ws.Holder (I changed WSDL according to: CXF generate Holders if there is multiple outputs or the input/output message contain the same message part).
And now it works (response.get() returns instance of MyMessage).
But still it would be nice if somebody could tell me the solution with Holder.

Removing namespace prefix in the output of Spring WS web service

I have created web service using Spring-WS. When I send a request to the web service, this is the response I get in soap-ui:
enter code here
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns2:SendResponse xmlns:ns2="http://mycompany.com/schema/">
<ns2:SendResult>
<ns2:Token>A00179-02</ns2:Token>
</ns2:SendResult>
</ns2:SendResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Is there any way to get rid of the "ns2" namespace prefix from the response? I tried a couple of options:
1) Manually updated package-info.java to set the prefix to "":
#XmlSchema(namespace = "http://mycompany.com/schema/",
xmlns = {
#XmlNs(namespaceURI = "http://mycompany.com/schema/", prefix = "")
},
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.example.foo.jaxb;
2) Set the prefix to "" in the QName object in the endpoint class:
return new JAXBElement<SendAndCommitResponse>(new QName("http://mycompany.com/schema/",
"SendResponse",""), SendResponse.class, response);
Both didn't work. How to get rid off the "ns2" namespace prefix?
I eventually found a solution for this.
My problem was caused by JDK 6 not shipping a full version of rt.jar (http://www.oracle.com/technetwork/java/javase/compatibility-137541.html).
I added the following to my maven config
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.6</version>
</dependency>
And then added
#XmlSchema(namespace = "http://mycompany.com/schema/",
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.UNQUALIFIED).
In the package-info.java (like suggested by #acdcjunior above)
I tried a few of the approaches discussed here, but nothing worked...
Below Class from the link - https://zhuanlan.zhihu.com/p/35298171 fixed my issue
Added the below interceptor to remove the namespaces -
public class PayloadPrefixInterceptor extends TransformerHelper implements EndpointInterceptor {
public static final String NAMESPACE = ObjectFactory.class.getPackage().getAnnotation(XmlSchema.class).namespace();
public static final String XMLNS = "xmlns:";
#Override
public boolean handleRequest(MessageContext messageContext, Object endpoint) throws Exception {
return true;
}
#Override
public boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
WebServiceMessage response = messageContext.getResponse();
Source payloadSource = response.getPayloadSource();
DOMResult result = new DOMResult();
transform(payloadSource, result);
removePrefix(result.getNode());
transform(new DOMSource(result.getNode()), response.getPayloadResult());
return true;
}
private void removePrefix(Node node) {
if (node == null) {
return;
}
if (node.getNodeType() == Node.ELEMENT_NODE) {
removeNamespaceDeclaration(node);
}
if (node.getPrefix() != null) {
node.setPrefix(null);
}
NodeList childNodes = node.getChildNodes();
if (childNodes != null) {
IntStream.of(0, childNodes.getLength()).forEach(index -> removePrefix(childNodes.item(index)));
}
Node nextSibling = node.getNextSibling();
if (nextSibling != null) {
removePrefix(nextSibling);
}
}
private void removeNamespaceDeclaration(Node node) {
NamedNodeMap attributes = node.getAttributes();
IntStream.range(0, attributes.getLength()).forEach(index -> {
Node attribute = attributes.item(index);
if (StringUtils.startsWith(attribute.getNodeName(), XMLNS) &&
StringUtils.equals(attribute.getNodeValue(), NAMESPACE)) {
attributes.removeNamedItemNS(attribute.getNamespaceURI(), attribute.getLocalName());
}
});
}
#Override
public boolean handleFault(MessageContext messageContext, Object endpoint) throws Exception {
return true;
}
#Override
public void afterCompletion(MessageContext messageContext, Object endpoint, Exception ex) throws Exception {
}
}
Registered the interceptor using below -
#EnableWs
#Configuration
public class Config extends WsConfigurerAdapter {
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(new PayloadPrefixInterceptor());
super.addInterceptors(interceptors);
}
}
it was hard
first: create a class that intercepts soap request and responses:
package examples.webservices.handler;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class CorrigirConteudoRequisicaoSOAP implements SOAPHandler<SOAPMessageContext> {
public Set<QName> getHeaders() {
return Collections.emptySet();
}
public boolean handleMessage(SOAPMessageContext messageContext) {
this.corrigirConteudoRequisicaoSOAP(messageContext);
return true;
}
private void corrigirConteudoRequisicaoSOAP(SOAPMessageContext messageContext){
SOAPMessage msg = messageContext.getMessage();
try {
NodeList childNodes = msg.getSOAPBody().getChildNodes();
for(int k = 0; k < childNodes.getLength(); k++){
Node item = childNodes.item(k);
String localName = item.getLocalName();
{
item.setPrefix("");
Method m = SOAPElement.class.getDeclaredMethod("setElementQName", QName.class);
//I was forced to use reflection because the method setElementQname is not //visible, neither the class that implements it
m.invoke(item, new QName("", item.getLocalName()));
msg.saveChanges();
}
}
} catch (Exception e) {
try {
msg.writeTo(System.out);
} catch (Exception e1) {
e1.printStackTrace();
}
System.out.println();
}
}
public boolean handleFault(SOAPMessageContext messageContext) {
return true;
}
public void close(MessageContext messageContext) {
}
public static void main(String[] args)throws Exception {
}
}
second: associate the service to soap handle
public class PortalFornecedor {
public Usuario getUsuario(){
XIF367Afae09A3344Fbf2E1De819D6EcbaService classeComNomeFeio = new XIF367Afae09A3344Fbf2E1De819D6EcbaService();
Usuario service = classeComNomeFeio.getHTTPPort();
BindingProvider bp = (BindingProvider)service;
Map<String, Object> requestContext = bp.getRequestContext();
requestContext.put(BindingProvider.USERNAME_PROPERTY, "user");
requestContext.put(BindingProvider.PASSWORD_PROPERTY, "pass");
this.handle(service);
return service;
}
public Object getDashboard(){
return "";
}
// here we associate the service to soap handle
private BindingProvider handle(Usuario service) {
BindingProvider bp = (BindingProvider)service;
#SuppressWarnings("rawtypes")
List<Handler> chain = new ArrayList<Handler>();
chain.add(new CorrigirConteudoRequisicaoSOAP());
bp.getBinding().setHandlerChain(chain);
return bp;
}
public static void main(String[] args) {
PortalFornecedor pf = new PortalFornecedor();
Usuario usuario = pf.getUsuario();
LoginExecutarIN in = new LoginExecutarIN();
generated.Usuario user = new generated.Usuario();
user.setLogin("onias");
user.setSenha("12345");
user.setCodigoUsuario(0);
in.setParametroEntrada(user);
try {
LoginExecutarOUT out = usuario.loginExecutar(in);
// SOAPMessageContext.getMessage();
System.out.println(out.getRegistroSelecionado().getNome());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Here is the simple and easiest solution for that problem. Create Package-Info.Java file in your model package and add the below script to that.
#javax.xml.bind.annotation.XmlSchema(namespace = "http://mycompany.com/schema", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED, xmlns = { #javax.xml.bind.annotation.XmlNs(namespaceURI = "http://mycompany.com/schema", prefix = "") })
package my.com.scicom.stars.model;
And add elementFormDefault as "qualified" in your xsd or wsdl file.
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://mycompany.com/schema"
targetNamespace="http://mycompany.com/schema"
elementFormDefault="qualified">

Testing a Spring MVC controller method with Spring MockMvc* classes

I am trying to test the following Spring mvc controller method:
#RequestMapping(value = "/preferences/email", method = RequestMethod.POST, produces = "text/html")
public String modifyEmail(#ModelAttribute #Validated({ Validation.EmailModification.class }) EmailInfo emailInfo, BindingResult bindingResult, Model model, Locale locale) {
Member member = memberService.retrieveCurrentMember();
if (!preferencesService.isEmailAvailable(emailInfo.getEmail())) {
if (member.getEmail().equals(emailInfo.getEmail())) {
bindingResult.addError(new FieldError("emailInfo", "email", messageSource.getMessage("controller.preferences.same_email", null, locale)));
} else {
bindingResult.addError(new FieldError("emailInfo", "email", messageSource.getMessage("controller.preferences.email_already_used", null, locale)));
}
}
if (bindingResult.hasErrors()) {
model.addAttribute("emailInfo", emailInfo);
return "preferences";
}
preferencesService.modifyEmail(member, emailInfo.getEmail());
return "redirect:/preferences/email";
}
Here is the EmailInfo bean:
#RooEquals
#RooJavaBean
public class EmailInfo {
#NotNull(groups = { Validation.EmailModification.class })
#Pattern(regexp = "^[_a-z0-9-]+(\\.[_a-z0-9-]+)*#[a-z0-9-]+(\\.[a-z0-9-]+)+$", groups = { Validation.EmailModification.class })
private String email;
private boolean activated;
private String token;
}
Here is the test class:
#ContextConfiguration
#WebAppConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
public class PreferenceControllerTest {
#Autowired
private WebApplicationContext ctx;
private MockMvc mockMvc;
#Autowired
private MemberService memberService;
#Autowired
private PreferencesService preferencesService;
#Autowired
private MemberRepository memberRepository;
#Autowired
private SigninService signinService;
#Autowired
private MessageSource messageSource;
#Before
public void setup() {
mockMvc = webAppContextSetup(ctx).build();
Member currentMember = new Member();
currentMember.setEmail("currentMember#example.com");
when(memberService.retrieveCurrentMember()).thenReturn(currentMember);
when(preferencesService.isEmailAvailable("notAvailable#example.com")).thenReturn(Boolean.FALSE);
}
#Test
public void test() throws Exception {
mockMvc.perform(post("/preferences/email")//
.param("email", "newEmail#example.com"))//
.andDo(print()).andExpect(model().attributeHasNoErrors("emailInfo", "email"));
}
#Configuration
public static class testConfiguration {
#Bean
public PreferenceController preferenceController() {
return new PreferenceController();
}
#Bean
public PreferencesService preferenceService() {
return mock(PreferencesService.class);
}
#Bean
public MemberService memberService() {
return mock(MemberService.class);
}
#Bean
public MemberRepository memberRepository() {
return mock(MemberRepository.class);
}
#Bean
public SigninService signinService() {
return mock(SigninService.class);
}
#Bean
public MessageSource messageSource() {
return mock(MessageSource.class);
}
}
}
Curiously I get the following output:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /preferences/email
Parameters = {email=[newEmail#example.com]}
Headers = {}
Handler:
Type = com.bignibou.controller.PreferenceController
Async:
Was async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = preferences
View = null
Attribute = emailInfo
value = com.bignibou.controller.helpers.EmailInfo#9a56c123
errors = [Field error in object 'emailInfo' on field 'email': rejected value [null]; codes []; arguments []; default message [null]]
FlashMap:
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = preferences
Redirected URL = null
Cookies = []
The test fails with the above output and I am not sure why. I expected the test to pass as the email address is available.
Can anyone please help?
edit 1:
The following is not working either:
#Before
public void setup() {
mockMvc = webAppContextSetup(ctx).build();
Member currentMember = new Member();
currentMember.setEmail("currentMember#example.com");
when(memberService.retrieveCurrentMember()).thenReturn(currentMember);
when(preferencesService.isEmailAvailable(eq("notAvailable#example.com"))).thenReturn(Boolean.FALSE);
when(preferencesService.isEmailAvailable(eq("newEmail#example.com"))).thenReturn(Boolean.TRUE);
}
edit 2:
I was able to get is to work with the above edit 1 plus the test below:
#Test
public void test() throws Exception {
mockMvc.perform(post("/preferences/email")//
.param("email", "available#example.com"))//
.andDo(print())//
.andExpect(model().attributeHasNoErrors("emailInfo"));
}
With this :
.param("email", "newEmail#example.com"))//
You are setting request parameter to the string value. However you have not shown your conversion from String to EmailInfo.
In your test you are checking the field of emailInfo called email.
I am not sure what this is for ?
when(preferencesService.isEmailAvailable("notAvailable#example.com")).thenReturn(Boolean.FALSE);
What is supposed to do, you have injected your preferenceService using autowired.
Updae to answer comment.
in your controller try
String email=emailInfo.getEmail();
if(!preferencesService.isEmailAvailable(email))){ instead of if (!preferencesService.isEmailAvailable(emailInfo.getEmail())) {
Not sure, just a possible solution
Or try
when(preferencesService.isEmailAvailable(eq("newEmail#example.com"))).thenReturn(Boolean.TRUE);
when(preferencesService.isEmailAvailable(eq("notAvailable#example.com"))).thenReturn(Boolean.FALSE);
Ae you using Mockito to implement mocking?
I am not 100% sure but here is How I understand your code.
when(preferencesService.isEmailAvailable("notAvailable#example.com")).thenReturn(Boolean.FALSE);
if preferencesService.isEmailAvailable returns true then you are forcefully returning false in mock exercise
so when in mock exercise preferencesService.isEmailAvailable will always return false.
Now in your Controller
if (!preferencesService.isEmailAvailable(emailInfo.getEmail())) {
if (member.getEmail().equals(emailInfo.getEmail())) {
bindingResult.addError(new FieldError("emailInfo", "email", messageSource.getMessage("controller.preferences.same_email", null, locale)));
} else {
bindingResult.addError(new FieldError("emailInfo", "email", messageSource.getMessage("controller.preferences.email_already_used", null, locale)));
}
}
If preferencesService.isEmailAvailable is false then ! make it true so code will always go inside if Block , and you will get Field Error, and hence Test fails.