I have a dead simple FeignClient interface that I would like to "unit"/integration test with a fake HTTP server, WireMock for example. The idea is to test the mapping with a sampled HTTP API response, without configuring a whole Spring Boot/Cloud Context.
#FeignClient(name = "foo", url = "${foo.url}")
public interface FooClient {
#RequestMapping(value = "/foo/{foo-id}/bar", method = RequestMethod.GET)
public Bar getBar(#PathVariable("foo-id") String fooId);
}
Is there any way to programmatically instantiate this interface, like a Spring Data Repository through a *RepositoryFactoryBean ?
I see a FeignClientFactoryBean in the source code, but it is package protected, and it relies on an ApplicationContext object to retrieve its dependencies anyway.
Well, you can fake a real rest client using wiremock for testing purposes, but this is more about containing the functional test, that feign clients themself work. This is mostly not what you really want to test, because the actual need is to test your components using your client behave in a specified way.
The best practice for me is not to make live hard with maintaing a fake server, but mock the clients behavior with Mockito. If you use Spring Boot 1.4.0, here is the way to go:
Consider you have some FooBarService, which internally uses your FooClient to peform some FooBarService::someAction(String fooId), which performs some business logic which needs to work with a foo with given id
#RunWith(SpringRunner.class)
#SpringBootTest(classes = App.class)
class FooUnitTest {
#Autowired;
private FooBarService fooBarService;
#MockBean;
private FooClient fooClient;
#Test
public void testService() {
given(fooClient.getBar("1")).willReturn(new Bar(...));
fooBarService.someAction("1");
//assert here, that someAction did what it supposed to do for that bar
}
}
At this point you first should clarify, what you expect the REST client to respond, when asking for "/foo/1/bar", by creating a mock for exactly that case and give the Bar object you expect to receive for that API, and assert that your application is in the desired state.
Related
I have a code with two methods. Method A is calling method B. Should I mock method B? Or can I let method A call method B since there it's only buciness logic without datatabase connection or httprequests?
public Response InsertAsset(UpdateRequest apiRequest, String token) throws IOException, InterruptedException
{
/* TODO
* Change hard-coded URL implementation
*/
String url = "http://test:8080/update";
User user = userRepository.findByToken(token);
UpdateRequestRequest = new UpdateRequest();
generateRequestAPI(Request, user);
Request.setAsset(apiRequest.getAsset());
Request.setKey(generateCombinedKey(Request, user));
// Will throw NullPointerException in case HTTP body cannot be generated
HttpRequest httpRequest = generateHttpPostRequest(url, Request, token);
HttpResponse<String> httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
return objectMapper.readValue(httpResponse.body(), Response.class);
}
Edited because I had gotten the question wrong at first.
Short answer is: you may probably just use the generateHttpPostRequest().
Longer answer ...
The original answer:
Without knowing your code an answer is impossible. Mocks are for unit tests. In a unit test you have the system under test (SUT) and external dependencies. For a unit test you want to get rid of all behaviour in the dependecies and instead completely control what you SUT will see during the test. Also unit tests must be easy to read, hence complex configurations are a no.
Some hints for your decision:
Never mock the SUT!
If the dependency has no behaviour and you can easily determin what state it will present your SUT, you may not need to mock it.
Configuring a mock to return a mock may be needed sometimes but generally should be avoided, if possible.
I have an app that uses spring security 4.0 and i am concern now about the content negotiation response that this app could send on a REST web service i.e.
my target is to restrict the response on a global basis irrelevant of the type of the request i.e. if that would be REST http get request through MVC or some kind of websocket (although i am not sure if that apply for the websocket) the response should be only returned as a json and NOT as XML. I do not want to support xml or any negotiation format.
The reason i am concerned about this is because i watched
a video on infoq made by a gentlemen called Mike Wiesner about spring application security pitfalls.
i know i can use in this case the annotation #RequestMapping and the sub-option "produces", i.e. something like
#RequestMapping(produces={MediaType.APPLICATION_JSON_VALUE} , value = "/target/get", method=RequestMethod.GET)
but since i have so many controllers it will be a nightmare for me to put that additional sub-option on all of them.
and i know that there are other annotations such as
#XmlTransient
#JsonIgnore
that could help me with what i want to do i.e. make some filds (getter/setters) to not be exposed in case the content negotiation changes but putting those annotations on each
getter/setter will even be bigger problem
Thus my question how do i do that on a global basis. I suppose this should be done in the MVCConfig class that extends WebMvcConfigurerAdapter?
By that i mean overriding the configureContentNegotiation method There are multiple examples doing that but those only explaing how to set up the the default behavior. My question is how do we restrict the behavior i.e. if http request is coming with "Accept" header application/xml how do i reject that on a global basis.
examples of the default behavior:
Spring boot controller content negotiation
so what i do is someting like
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false).
If anything else then a json comms into the http request
reject this request or smply ignore it on a global basis.
Do not send/support xml, xhtml, html etc.
}
}
I coincidentally was looking into a related issue to this question in the last couple of days. We manually configure a ContentNegotiationManager in our code base, and in that process we limit the header based portion of the Spring PPA Strategy by providing an overridden HeaderContentNegotiationStrategy that does limiting by the Accept header similar to what you want. I took a quick look at ContentNegotiationConfigurer (which I have never used) and it does not appear to provide an option for which to alter mappings for the HeaderContentNegotiationStrategy, so here is a code snippet of the way we setup our ContentNegotiationManager.
#Bean
public ContentNegotiationManager contentNegotiationManager() {
//Supply a Map<String, org.springframework.http.MediaType>
PathExtensionContentNegotiationStrategy pathBased = new PathExtensionContentNegotiationStrategy(supportedMediaTypes());
//Supply a Map<org.springframework.http.MediaType, org.springframework.http.MediaType>
HeaderContentNegotiationStrategy headerBased = new MappingHeaderContentNegotiationStrategy(contentTypeMap());
FixedContentNegotiationStrategy defaultStrategy = new FixedContentNegotiationStrategy(MediaType.APPLICATION_JSON);
return ContentNegotiationManager(pathBased, headerBased, defaultStrategy);
return retval;
}
That bean is created in our config that overrides WebMvcConfigurerAdapter and is injected into this bean:
#Bean
#Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping();
handlerMapping.setOrder(0);
handlerMapping.setRemoveSemicolonContent(false);
handlerMapping.getFileExtensions().add("json");
handlerMapping.setUseRegisteredSuffixPatternMatch(true);
handlerMapping.setInterceptors(getInterceptors());
handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
return handlerMapping;
}
I'm newbie for JUnit test case. Please help me on this issue. I have 2 mule flows- first flow having MQ as inbound and it has datamapper to transformer the xml. With the first flow input, i'm calling second flow where we are calling the existing service ( SOAP/HTTP) call. Please find my JUnit below. I'm able to get the success response. But my requirement
1. I need to see the transformer response coming out from the Transformer.( Like how we see via logger component in our flow)
2.Need to override the url (HTTP) through JUnit ( in order to test the error scenario)
public class Request_SuccessPath extends FunctionalTestCase {
#Test
public void BulkRequest () throws Exception {
MuleClient client = muleContext.getClient();
System.out.println("test");
String payload = " <root> <messageName>str1234</messageName><messageId>12345</messageId><DS>123</DS><</root>";
MuleMessage reply = client.send ("vm://test",payload ,null);}
#Override
protected String getConfigResources() {
// TODO Auto-generated method stub
return "src/main/app/project.xml";}
i thought the following snippet will override the url.But it is not
DefaultHttpClient client1 = new DefaultHttpClient();
HttpGet httpGet = new HttpGet("http://localhost:7800/service);
assertNotNull(response);
3. How to take the control of the flow and see any response inbetween the flow.
Instead of WMQ, i have replaced VM as inbound end point for testing purposes.
4. Is there any chance like without replacing VM can we call directly with WMQ through JUnit TestCase. Kindly help me on this.
I'm using 3.4 version, not using maven as of now. Please help me. Thanks in advance.
1) What do you mean by "see". Would it work logging it? inspecting it while debugging?
2) You should parametrize your endpoint with a variable, something like
and configure a property placeholder as explained here: http://www.mulesoft.org/documentation/display/current/Using+Parameters+in+Your+Configuration+Files
Adding http.port, http.host and http.path variables to mule-app.properties
taking into account that you must set system-properties-mode="OVERRIDE" and then start your Mule server using bin/mule -M-Dhttp.host=your-host -M-Dhttp.port=your-port -M-Dhttp.path=your-path
3) Yes, WMQ has a Java API you can use to interact with it: http://publib.boulder.ibm.com/infocenter/wmqv6/v6r0/index.jsp?topic=%2Fcom.ibm.mq.csqzaw.doc%2Fuj41013_.htm , you will probably found hundreds of examples by googling about it.
Regards.
I have a Grails service that sends out e-mails using a 3rd-party service by doing a HTTP call:
class EmailService {
def sendEmail(values) {
def valueJson = values as JSON
... // does HTTP call to 3rd party service
}
}
I've written a unit test to test this service (because an integration test spins up Hibernate and the entire domain framework, which I don't need):
#TestFor(EmailService)
class EmailServiceTests {
void testEmailServiceWorks() {
def values = [test: 'test', test2: 'test2']
service.sendEmail(values)
}
}
However, when I execute this unit test, it fails with this exception when it tries to do the as JSON conversion:
org.apache.commons.lang.UnhandledException: org.codehaus.groovy.grails.web.converters.exceptions.ConverterException: Unconvertable Object of class: java.util.LinkedHashMap
I then re-wrote my unit test to just do the following:
void testEmailServiceWorks() {
def value = [test: 'test', test2: 'test2']
def valueJson = value as JSON
}
And I get the same exception when it tries to do the as JSON conversion.
Does anyone know why I'm getting this exception, and how I can fix it?
Even though you are testing a service, you can apply the #TestMixin(ControllerUnitTestMixin) annotation to your test class to get Grails to set up the JSON converter.
The as JSON magic is created when the domain framework spins up.
You have to either change your test to an integration one or mock the asType.
def setUp(){
java.util.LinkedHashMap.metaClass.asType = { Class c ->
new grails.converters."$c"(delegate)
}
}
Rember to clean up after yourself in the tearDown, you wouldn't want metaprogramming leaks in your test suite.
def tearDown(){
java.util.LinkedHashMap.metaClass.asType = null
}
Edit:
If you come from the future, consider this answer: https://stackoverflow.com/a/15485593/194932
As Grails 3.3.x grails-test-mixins plugin is deprecated. #see migration guide.
For this problem you should implement GrailsWebUnitTest which is coming from Grails Testing Support Framework.
you can initialise the JSON in the setUp() . There are various marshallers which implement ObjectMarshaller , which need to be added to the ConverterConfiguration for JSON conversion to work.
http://grails.github.io/grails-doc/2.4.4/api/index.html?org/codehaus/groovy/grails/web/converters/marshaller/json/package-summary.html
example :
DefaultConverterConfiguration<JSON> defaultConverterConfig = new DefaultConverterConfiguration<JSON>()
defaultConverterConfig.registerObjectMarshaller(new CollectionMarshaller())
defaultConverterConfig.registerObjectMarshaller(new MapMarshaller())
defaultConverterConfig.registerObjectMarshaller(new GenericJavaBeanMarshaller())
ConvertersConfigurationHolder.setTheadLocalConverterConfiguration(JSON.class, defaultConverterConfig);
I just ran into this, and I really didn't want to implement GrailsWebUnitTest as recommended in another answer here. I want to keep my service test as "pure" and lean as possible. I ended up doing this:
void setupSpec() {
defineBeans(new ConvertersGrailsPlugin())
}
void cleanupSpec() {
ConvertersConfigurationHolder.clear()
}
This is how it happens under the hood when you implement GrailsWebUnitTest (via WebSetupSpecInterceptor and WebCleanupSpecInterceptor).
That said, the converters seem to be meant for use in the web tier, primarily for making it easy to transparently return data in different formats from a controller. It's worth considering why the service you're testing needs the converters in the first place.
For example, in my case, someone used the JSON converter to serialize some data to a string so it could be stored in a single field in the database. That doesn't seem like an appropriate user of the converters, so I plan on changing how it's done. Making the converters available in my service test is a temporary solution to allow me to improve our test coverage before I refactor things.
I was getting the same error when trying to unit test a controller that calls "render myMap as JSON". We use Grails 1.3.7 and none of the other solutions worked for me without introducing other problems. Upgrading Grails was not an alternative for us at the moment.
My solution was to use JSONBuilder instead of "as JSON", like this:
render(contentType: "application/json", {myMap})
See http://docs.grails.org/latest/guide/theWebLayer.html#moreOnJSONBuilder
(I realize this is old, but came here in search for a solution and so might others)
I've got a bit of a problem. I'm working in the Castle Windsor IOC Container. Now what i wanted to do is just mess about with some AOP principles and what i specifically want to do is based on a method name perform some logging. I have been looking at Interceptors and at the moment i am using the IInterceptor interface implemented as a class to perform this logging using aspects. The issue is if i want to perform the logging on a specific method then it gets messy as i need to put in some logic into my implemented aspect to check the method name etc...
I have read that you can do all of this using Dynamic Proxies and the IInterceptorSelector interface and the IProxyGenerationHook interface. I have seen a few examples of this done on the net but i am quite confused how this all fits into the Windsor container. I mean i am using the windsor container which in my code is actually a ref to the IWindsorContainer interface to create all my objects. All my configuration is done in code rather than XML.
Firstly does anyone know of a way to perform method specific AOP in the windsor container besides the way i am currently doing it.
Secondly how do i use the Dynamic Proxy in the windsor container ?
Below i have added the code where i am creating my proxy and registering my class with
the interceptors
ProxyGenerator _generator = new ProxyGenerator(new PersistentProxyBuilder());
IInterceptorSelector _selector = new CheckLoggingSelector();
var loggingAspect = new LoggingAspect();
var options = new ProxyGenerationOptions(new LoggingProxyGenerationHook())
{ Selector = _selector };
var proxy = _generator.CreateClassProxy(typeof(TestClass), options, loggingAspect);
TestClass testProxy = proxy as TestClass;
windsorContainer.Register(
Component.For<LoggingAspect>(),
Component.For<CheckLoggingAspect>(),
Component.For<ExceptionCatchAspect>(),
Component.For<ITestClass>()
.ImplementedBy<TestClass>()
.Named("ATestClass")
.Parameters(Parameter.ForKey("Name").Eq("Testing"))
.Proxy.MixIns(testProxy));
The Test Class is below:
public class TestClass : ITestClass
{
public TestClass()
{
}
public string Name
{
get;
set;
}
public void Checkin()
{
Name = "Checked In";
}
}
as for the interceptors they are very simple and just enter a method if the name starts with Check.
Now when i resolve my TestClass from the container i get an error.
{"This is a DynamicProxy2 error: Mixin type TestClassProxy implements IProxyTargetAccessor which is a DynamicProxy infrastructure interface and you should never implement it yourself. Are you trying to mix in an existing proxy?"}
I know i'm using the proxy in the wrong way but as i haven't seen any concrete example in how to use a proxy with the windsor container it's kind of confusing.
I mean if i want to use the LoggingProxyGenerationHook which just tell the interceptors to first for methods that start with the word "check" then is this the correct way to do it or am i completely on the wrong path. I just went down the proxy way as it seems very powerfull and i would like to understand how to use these proxies for future programming efforts.
By using .Interceptors() you already are using Dynamic Proxy. When component has specified interceptors Windsor will create proxy for it, and use these interceptors for it. You can also use method .SelectedWith and .Proxy property to set other options you already know from DynamicProxy.
I just added a website about Windsor AOP to documentation wiki. There's not much there yet, but I (and Mauricio ;) ) will put there all the information you need. Take a look, and let us know if everything is clear, and if something is missing.