CXF soap application, using following versions:
springBootVersion = 1.2.3.RELEASE
springVersion = '4.1.6.RELEASE'
cxfVersion = '3.1.0'
junitVersion = '4.12'
I have a spring bean with a request scope:
#Component
#Scope( value=WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS )
public class RequestScopedClass
which I fetch dynamically from ApplicationContext in my CXF endpoint implementation:
#Component
#WebService(endpointInterface = "ch.xyz.PaymentServiceInterface" )
public class PaymentServiceImpl implements PaymentServiceInterface
{
...
RequestScopedClass rsc = appCtxt.getBean( RequestScopedClass.class );
rsc.doSomething();
My goal is to test the frontend of the soap service by simulating a client which connects to the listener port etc., ensuring that the whole cxf stack with the interceptor chain (including my custom interceptors) is executed. I managed to setup this configuration by including the
org.apache.cxf:cxf-rt-transports-http-jetty
dependency and starting the endpoint in test setup:
String address = "http://0.0.0.0:8080/";
myEndpoint = Endpoint.publish( address, new PaymentServiceImpl() );
Running the test throws BeanCreationException in call to rsc.doSomething():
Error creating bean with name 'scopedTarget.requestScopedEnvironment': Scope 'request' is not active ...
If I change the proxyMode to one of the three other possibilites, the same exception is already thrown when fetching the bean from the appCtxt.
The test is annotated by
#RunWith( SpringJUnit4ClassRunner.class )
#ContextConfiguration( classes = {
...,
RequestScopedClass.class
}
)
#WebAppConfiguration
If the application is started by "gradle bootrun" on command line and the soap request is done by the chrome postman application everything is fine and I get the expected soap response.
What can I do that there is a valid request scope when executing the cxf soap server in a unit test?
Meanwhile I found the solution:
...
import org.springframework.context.ConfigurableApplicationContext;
#Autowired
private ConfigurableApplicationContext myCtxt;
#Before
public void setUp() throws Throwable
{
myCtxt.getBeanFactory().registerScope( "session", new CustomScope4Test() );
myCtxt.getBeanFactory().registerScope( "request", new CustomScope4Test() );
}
public class CustomScope4Test implements Scope
{
private final Map<String, Object> beanMap = new HashMap<String, Object>();
/**
* #see org.springframework.beans.factory.config.Scope#get(java.lang.String, org.springframework.beans.factory.ObjectFactory)
*/
public Object get( String name, ObjectFactory<?> factory )
{
Object bean = beanMap.get( name );
if ( null == bean )
{
bean = factory.getObject();
beanMap.put( name, bean );
}
return bean;
}
/**
* #see org.springframework.beans.factory.config.Scope#getConversationId()
*/
public String getConversationId()
{
// not needed
return null;
}
/**
* #see org.springframework.beans.factory.config.Scope#registerDestructionCallback(java.lang.String, java.lang.Runnable)
*/
public void registerDestructionCallback( String arg0, Runnable arg1 )
{
// not needed
}
/**
* #see org.springframework.beans.factory.config.Scope#remove(java.lang.String)
*/
public Object remove( String obj )
{
return beanMap.remove( obj );
}
/**
* #see org.springframework.beans.factory.config.Scope#resolveContextualObject(java.lang.String)
*/
public Object resolveContextualObject( String arg0 )
{
// not needed
return null;
}
}
Related
I am trying to create the simple web api test for the controller action method I have in my project. I already create and add the test project in my solution. And add the Nunit nuget package in test project.
The controller I am trying to test is look like this:
[ApiController]
[Route("[controller]")]
public class HomeController : ControllerBase
{
private readonly IConfiguration _configuration;
private readonly IHostEnvironment _hostEnvironment;
private readonly ILogger<HomeController> _logger;
private BaseDataAccess _datatAccess = new BaseDataAccess()
public HomeController(ILogger<HomeController> logger, IConfiguration configuration, IHostEnvironment hostEnvironment)
{
_logger = logger;
_configuration = configuration;
_hostEnvironment = hostEnvironment;
}
[HttpGet("GetInfo/{code}")]
public IActionResult GetInfo(string code)
{
List<InfoModel> infos = new List<InfoModel>();
int isNumber;
if (String.IsNullOrEmpty(code) || !int.TryParse(code, out isNumber))
{
_logger.LogInformation(String.Format("The code pass as arguments to api is : {0}", code));
return BadRequest("Invalid code");
}
try
{
_logger.LogDebug(1, "The code passed is" + code);
SqlConnection connection = _datatAccess.GetConnection(_configuration, _hostEnvironment);
string sql = string.Format ("SELECT * from table1 where code={0}", code);
DataTable dt = _datatAccess.ExecuteQuery(connection,CommandType.Text, sql);
if (dt != null && dt.Rows.Count > 0)
{
foreach (DataRow dr in dt.Rows)
{
infos.Add(new InfoModel
{
ID = dr["id"].ToString(),
code = dr["code"].ToString()
});
}
}
}
catch (Exception ex)
{
_logger.LogError(4, String.Format("Error Message: " + ex.Message + "\n" + ex.StackTrace));
return BadRequest("There is something wrong.Please contact the administration.");
}
return new OkObjectResult(infos);
}
}
Now when I try to create the unit test I need to pass the configuration, hostenvironment and logger to HomeController from my TestHomeController. And I don't know how to instantiate these settings and pass to controller:
using NUnit.Framework;
using Microsoft.AspNetCore.Mvc;
using MyApi.Models;
using MyApi.Controllers;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace MyApi.Tests
{
[TestFixture]
public class TestHomeController: ControllerBase
{
private readonly IConfiguration _configuration; //How to instantiate this so it is not null
private readonly IHostEnvironment _hostEnvironment ;//How to instantiate this so it is not null
private ILogger<HomeController> _logger;//How to instantiate this so it is not null
[Test]
public void GetInfo_ShouldReturnAllInfo()
{
var controller = new HomeConteoller(_logger, _configuration, _hostEnvironment);
var result = controller.GetInfo("11");
var okObjectResult = (OkObjectResult)result;
//Assert
okObjectResult.StatusCode.Equals(200);
}
}
}
Thanks for any help and suggestions.
Probably, you have startup.cs. Don't you?
if you gonna test a controller, then you need to build a whole instance of an application. Here I put an example of how you can test your code if you have Startup.cs.
public class SUTFactory : WebApplicationFactory<Startup>
{
protected override IHostBuilder CreateHostBuilder()
{
return Program.CreateHostBuilder(null);
}
}
public class TestControllerTests
{
private SUTFactory factory;
private HttpClient _client;
public TestControllerTests()
{
factory = new SUTFactory();
_client = factory.CreateClient();
}
[Test]
public async Task GetPatientInterviewID_ShouldReturnAllInterviewID()
{
// Arrange
var id = "11";
// Act
var result = await _client.GetAsync($"Home/GetInfo/{id}");
// Assert
Assert.AreEqual(System.Net.HttpStatusCode.OK, result.StatusCode);
}
}
This example is closer to Integration testing rather than Unit-testing. If you want to have unit-test then you need to do the following things
BaseDataAccess _datatAccess this is a specific realization and it cannot be mocked (comparing to ILogger, IHostEnvironment etc)
move all your code from the controller to a separate class, and test this class.
I am using AEM Mocks to test a custom servlet that uses a configuration, as such:
#Activate
void activate(final Config config) { ... }
I am following the approach described here and here to register and inject the service together with a HashMap, as such:
private static Map<String, Object> myHashMap = new HashMap<>();
...
myHashMap.put("a", "b");
myHashMap.put("c", "d");
...
servlet = context.registerInjectActivateService(new MyServlet(), myHashMap);
However, this approach doesn't work. The config object passed above, inside the activate function, is corrupted. For every key-value pair above, it sets null as the value. So instead of:
a -> b
c -> d
It sets:
a -> null
c -> null
Inside the HashMap. Can anyone please help? Thanks!
P.S. I should add that I am using version 2.3.0 of AEM Mocks since the recent versions cause an issue with an older artifact. For more info on that, see here.
I tested it, and it works with version 2.3.0 too. Could you check the following example? After that, it is probably a maven issue. Then we would need more information.
Here is my test servlet:
#Component(service = Servlet.class,
property = {
SLING_SERVLET_PATHS + "=/bin/servlet/test",
SLING_SERVLET_METHODS + "=GET",
SLING_SERVLET_EXTENSIONS + "=text"
})
#Designate(ocd = TestServlet.Config.class)
public class TestServlet extends SlingSafeMethodsServlet {
#ObjectClassDefinition
public #interface Config {
#AttributeDefinition(
name = "Name",
description = "Name used in the hello world text"
)
String name() default "Alex";
#AttributeDefinition(
name = "Greeting",
description = "Greeting - Morning, to demonstrate the dot-replacement"
)
String greeting_morning() default "Good Morning";
}
private Config config;
#Override
protected void doGet(#Nonnull SlingHttpServletRequest request, #Nonnull SlingHttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
response.getWriter().println(this.getGreeting());
}
public String getGreeting() {
return config.greeting_morning() + ", " + config.name();
}
#Activate
void activate(final Config config) {
this.config = config;
}
}
Here is a JUnit 4 test:
public class TestServletTest {
#Rule
public final AemContext context = new AemContext();
#Test
public void testWithoutConfig() {
final TestServlet testServlet = context.registerInjectActivateService(new TestServlet());
assertEquals("Good Morning, Alex", testServlet.getGreeting());
}
#Test
public void testWithConfig() {
final Map<String, Object> properties = new HashMap<>();
properties.put("name", "Berndt");
properties.put("greeting.morning", "Keep sleeping");
final TestServlet testServlet = context.registerInjectActivateService(new TestServlet(), properties);
assertEquals("Keep sleeping, Berndt", testServlet.getGreeting());
}
}
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());
}
}`
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.
I have this EJB
#Stateless
public class HelloBean{
public String sayHello(){
return "hello";
}
}
When I test with this unit test:
#Test
public void testEmbeddedPersistence() throws NamingException {
assertTrue(true);
Map<String, Object> props = new HashMap<String, Object>();
props.put(EJBContainer.MODULES, new File("target/classes"));
props.put("org.glassfish.ejb.embedded.glassfish.instance.root", "./src/test/resources/glassfish-testing-domain");
ec = EJBContainer.createEJBContainer(props);
ctx = ec.getContext();
String s = "java:global/classes/HelloBean";
HelloBean helloBean = (HelloBean) ctx.lookup(s);
assertNotNull(helloBean);
assertABunchOfStuff();
ec.close();
}
everything works fine.
But when I change it to
#Stateless
public class HelloBean implements RemoteHello{
public String sayHello(){
return "hello";
}
}
#Remote
public interface RemoteHello{
public String sayHello();
}
#Test
public void testEmbeddedPersistence() throws NamingException {
assertTrue(true);
Map<String, Object> props = new HashMap<String, Object>();
props.put(EJBContainer.MODULES, new File("target/classes"));
props.put("org.glassfish.ejb.embedded.glassfish.instance.root", "./src/test/resources/glassfish-testing-domain");
ec = EJBContainer.createEJBContainer(props);
ctx = ec.getContext();
String s = "java:global/classes/HelloBean!com.mycompany.remoteInterface.RemoteHello";
RemoteHello remoteHello = (RemoteHello) ctx.lookup(s);
assertNotNull(remoteHello);
assertABunchOfStuff();
ec.close();
}
I get a javax.naming.NamingException
the weirdest thing is, When the EJBContainer is intializing it says:
INFO: EJB5181:Portable JNDI names for EJB HelloBean: [java:global/classes/HelloBean!com.mycompany.remoteInterface.RemoteHello, java:global/classes/HelloBean]
Feb 10, 2012 3:55:22 PM com.sun.ejb.containers.BaseContainer initializeHome
followed shortly after:
Tests in error:
testEmbeddedPersistence(com.mycompaony.HelloBean): Lookup failed for 'java:global/classes/HelloBean!com.mycompany.remoteInterface.RemoteHello' in SerialContext[myEnv={java.naming.factory.initial=com.sun.enterprise.naming.impl.SerialInitContextFactory, java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl, java.naming.factory.url.pkgs=com.sun.enterprise.naming}
How can I get a successful jndi lookup with a remote interface.
Thanks
Remote interfaces are not part of EJB lite (EJB 3.1 specification, table 27), and embeddable containers are only required to provide EJB lite (EJB 3.1 specification, section 22.3.1).