Background: In several Java frameworks like Spring there is the possibility to have methods that are called with injected parameter values. A good example is a controller action in Spring Web/MVC that receives a POST value and has a redirect attribute.
Example:
#RequestMapping(value = "/testform", method = RequestMethod.POST)
public ModelAndView testform(#RequestParam(value = "postvalue") String postvalue, RedirectAttributes attributes)
{
if(postvalue.equals("Test"))
{
// Do stuff with attributes
}
return new ModelAndView("addresses");
}
For example when I would like to use something similar in an own application (No Spring included available) - I end up with something like that (Hacked together):
Strign actionname = "mymethod";
Controller controller = new SampleController();
for(Method method : controller.getClass().getDeclaredMethods())
{
String name = method.getName();
if(name.equals(actionname))
{
int parametercount = method.getParameterCount();
if(parametercount == 0) // No parameter
{
ModelAndView view = (ModelAndView) method.invoke(controller);
// Do stuff
}
else if(parametercount == 1) // 1 String parameter
{
ModelAndView view = (ModelAndView) method.invoke(controller, new String("parameter1"));
// Do stuff
}
else if(parametercount == 2) // 2 String parameters
{
ModelAndView view = (ModelAndView) method.invoke(controller, new String("parameter1"), new String("parameter2"));
// Do stuff
}
else // Error
{
// Unsupported method
}
break;
}
}
Problem: This solution only supports void, 1-parameter and 2-parameter methods that take a string as argument - nothing else
Question: How does Java and Spring allow such a feature? Does Spring have a huge array of method.invoke(...) that are suitable for the most common methods or is there a more dynamic solution to this problem?
Final solution: I ended up with this (unfinished) solution based on Seelenvirtuose answer:
else if(parametercount == 2)
{
Object[] parameters = new Object[2];
parameters[0] = new String("Hello");
parameters[1] = new String("world!");
method.invoke(controller, parameters);
}
Aside from any injection dependency frameworks in general (and Spring specifically), you seem to ask how to reflectively call methods with an arbitrary number of parameters.
As you can see in the invoke method's signature, you provide all parameters in an array. So you simply should assemble an argument array and provide that:
Object[] arguments = createArguments(parametercount); // create array of correct size
(ModelAndView) method.invoke(controller, arguments);
Note, that varargs are treated like an arry. Oh, and please respect the comments about string behaviors.
In principle Spring does the same thing, just more sophisticated.
Especially they don't look for names (at least for many things) but for annotations. You can get the annotations of classes, methods, fields and so on.
The other thing they'll use is that invoke takes an vararg, which is basically an array, so instead of having one if branch for each number of parameters, they pass just an array with the correct number of elements.
Related
I am trying to unit test a Java class with a method containing a lambda function. I am using Groovy and Spock for the test. For proprietary reasons I can't show the original code.
The Java method looks like this:
class ExampleClass {
AsyncHandler asynHandler;
Component componet;
Component getComponent() {
return component;
}
void exampleMethod(String input) {
byte[] data = input.getBytes();
getComponent().doCall(builder ->
builder
.setName(name)
.data(data)
.build()).whenCompleteAsync(asyncHandler);
}
}
Where component#doCall has the following signature:
CompletableFuture<Response> doCall(Consumer<Request> request) {
// do some stuff
}
The groovy test looks like this:
class Spec extends Specification {
def mockComponent = Mock(Component)
#Subject
def sut = new TestableExampleClass(mockComponent)
def 'a test'() {
when:
sut.exampleMethod('teststring')
then:
1 * componentMock.doCall(_ as Consumer<Request>) >> { args ->
assert args[0].args$2.asUtf8String() == 'teststring'
return new CompletableFuture()
}
}
class TestableExampleClass extends ExampleClass {
def component
TestableExampleClass(Component component) {
this.component = component;
}
#Override
getComponent() {
return component
}
}
}
The captured argument, args, shows up as follows in the debug window if I place a breakpoint on the assert line:
args = {Arrays$ArrayList#1234} size = 1
> 0 = {Component$lambda}
> args$1 = {TestableExampleClass}
> args$2 = {bytes[]}
There are two points confusing me:
When I try to cast the captured argument args[0] as either ExampleClass or TestableExampleClass it throws a GroovyCastException. I believe this is because it is expecting Component$Lambda, but I am not sure how to cast this.
Accessing the data property using args[0].args$2, doesn't seem like a clean way to do it. This is likely linked to the casting issue mentioned above. But is there a better way to do this, such as with args[0].data?
Even if direct answers can't be given, a pointer to some documentation or article would be helpful. My search results discussed Groovy closures and Java lambdas comparisons separately, but not about using lambdas in closures.
Why you should not do what you are trying
This invasive kind of testing is a nightmare! Sorry for my strong wording, but I want to make it clear that you should not over-specify tests like this, asserting on private final fields of lambda expressions. Why would it even be important what goes into the lambda? Simply verify the result. In order to do a verification like this, you
need to know internals of how lambdas are implemented in Java,
those implementation details have to stay unchanged across Java versions and
the implementations even have to be the same across JVM types like Oracle Hotspot, OpenJ9 etc.
Otherwise, your tests break quickly. And why would you care how a method internally computes its result? A method should be tested like a black box, only in rare cases should you use interaction testing,where it is absolutely crucial in order to make sure that certain interactions between objects occur in a certain way (e.g. in order to verify a publish-subscribe design pattern).
How you can do it anyway (dont!!!)
Having said all that, just assuming for a minute that it does actually make sense to test like that (which it really does not!), a hint: Instead of accessing the field args$2, you can also access the declared field with index 1. Accessing by name is also possible, of course. anyway, you have to reflect on the lambda's class, get the declared field(s) you are interested in, make them accessible (remember, they are private final) and then assert on their respective contents. You could also filter by field type in order to be less sensitive to their order (not shown here).
Besides, I do not understand why you create a TestableExampleClass instead of using the original.
In this example, I am using explicit types instead of just def in order to make it easier to understand what the code does:
then:
1 * mockComponent.doCall(_ as Consumer<Request>) >> { args ->
Consumer<Request> requestConsumer = args[0]
Field nameField = requestConsumer.class.declaredFields[1]
// Field nameField = requestConsumer.class.getDeclaredField('arg$2')
nameField.accessible = true
byte[] nameBytes = nameField.get(requestConsumer)
assert new String(nameBytes, Charset.forName("UTF-8")) == 'teststring'
return new CompletableFuture()
}
Or, in order to avoid the explicit assert in favour of a Spock-style condition:
def 'a test'() {
given:
String name
when:
sut.exampleMethod('teststring')
then:
1 * mockComponent.doCall(_ as Consumer<Request>) >> { args ->
Consumer<Request> requestConsumer = args[0]
Field nameField = requestConsumer.class.declaredFields[1]
// Field nameField = requestConsumer.class.getDeclaredField('arg$2')
nameField.accessible = true
byte[] nameBytes = nameField.get(requestConsumer)
name = new String(nameBytes, Charset.forName("UTF-8"))
return new CompletableFuture()
}
name == 'teststring'
}
Edit: I ended up solving the problem myself through more experimentation. The code seems quite verbose though so there is probably a better solution that doesn't involve typecasting strings to other things.
Answer posted below.
For my school work, I am supposed to create a GET mapping to receive a list of all entities of a specific type. This GET mapping should return simply all the entities if no parameter is provided, or otherwise it will apply something in the entity repository to use a JPQL query and the provided parameter which is used as an ordinal query parameter to filter the returned results.
"If no request parameter is provided, the existing functionality of returning all events
should be retained.
If more than one request parameter is specified, an error response should be
returned, indicating that at most one parameter can be provided.
If the ‘?status=XXX’ parameter is provided, with a status string value that does not
match the AEventStatus enumeration, an appropriate error response should be
returned."
I have tried to alter my GET mapping to have 3 optional #RequestParameter variables, but I found out that it is tedious logic-wise to check for the existence of multiple or no parameters, and then do something again based on the existence of which parameter is there.
Instead I tried this (I was in the middle of this and it is not complete):
#RequestMapping(
value="/aevents",
method = RequestMethod.GET)
public ResponseEntity<Object> getAllAEvents(HttpServletRequest request) {
if (request.getParameterMap().size() == 0) {
return ResponseEntity.status(HttpStatus.OK).body(repository.findAll());
}
if (request.getParameterMap().size() > 1) {
return new ResponseEntity<>("Can only handle one request paramter: title=, status= or minRegistrastions=", HttpStatus.BAD_REQUEST);
}
//incomplete from here
}
And I am now not sure if this is the correct approach or if I am simply overlooking something. I suppose I might be able to check for the names of the parameters that were provided in the request and then return a bad request response again if I find something that isn't a valid parameter. But I am not sure how to check the parameter map for the names of the parameters or if the parameter map even does what I think it does.
The parameters provided are supposed to be either an int, a value from an enum or a string.
Am I overlooking a simpler way to do this? i.e. a way to check the amount of parameters and the existence of parameters in a signature like:
#GetMapping("/aevents")
public List<AEvent> getAllAEvents(#RequestParam(required = false) String title,
#RequestParam(required = false) AEventStatus status,
#RequestParam(required = false) int minRegistrations) {
//Do something here
}
Or is my current approach feasible, and if it is, how do I continue on it?
Yes, you would likely do it your way, though:
You can inject the map of parameters directly in Spring.
Throw a ResponseStatusException (available since Spring 5) instead of fumbling around with the ResponseEntity.
#GetMapping("/aevents")
public List<AEvent> getAllAEvents( #RequestParam Map<String, String> allRequestParams){
if(allRequestParams.size() >1){
throw new ResponseStatusException(
HttpStatus.BAD_REQUEST,"too many params");
}
// do something
return list;
}
Answer to my own question after solving it:
What is the parameter map?
The parameterMap is actually a map of string keys and string arrays. To get the value for a parameter name (the key) you can get values of this key and then access it like an array.
However, using the parameterMap was not necessary. Instead it was better to just use the built-in Spring way of doing it which is by using the #RequestParam annotation with simply a #RequestParam Map<String, String> params in the method body.
Credits to https://stackoverflow.com/users/2255293/marco-behler for giving me an idea as well as providing a better way to throw exceptions.
#GetMapping("/aevents")
public List<AEvent> getAllAEvents(#RequestParam Map<String, String> params) {
if (params.size() == 0) { //Default case, no params
return repository.findAll();
}
if (params.size() > 1) { //Refuse to handle more than one provided param
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Can only handle one request parameter: title=, status= or minRegistrations=");
}
if (params.containsKey("title")) {
String value = params.get("title");
return repository.findByQuery("AEvent_find_by_title", ("%" + value + "%"));
}
if (params.containsKey("status")) {
String stringValue = params.get("status").toUpperCase();
for (AEventStatus e : AEventStatus.values()) {
if (e.name().equals(stringValue)) {
AEventStatus value = AEventStatus.valueOf(stringValue);
return repository.findByQuery("AEvent_find_by_status", value);
}
}
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "status=" + stringValue + " is not a valid AEvent status value.");
}
if (params.containsKey("minRegistrations")) {
int value;
try {
value = Integer.parseInt(params.get("minRegistrations"));
} catch (NumberFormatException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Provided request parameter was not a valid number.");
}
return repository.findByQuery("AEvent_find_by_minRegistrations", value);
}
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid query parameters.");
}
I'm putting more attention into unit tests these days and I got in a situation for which I'm not sure how to make a good test.
I have a function which creates and returns an object of class X. This X class is part of the framework, so I'm not very familiar with it's implementation and I don't have freedom as in the case of my "regular collaborator classes" (the ones which I have written). Also, when I pass some arguments I cannot check if object X is set to right parameters and I'm not able to pass mock in some cases.
My question is - how to check if this object was properly created, that is, to check which parameters were passed to its constructor? And how to avoid problem when constructor throws an exception when I pass a mock?
Maybe I'm not clear enough, here is a snippet:
public class InputSplitCreator {
Table table;
Scan scan;
RegionLocator regionLocator;
public InputSplitCreator(Table table, Scan scan, RegionLocator regionLocator) {
this.table = table;
this.scan = scan;
this.regionLocator = regionLocator;
}
public InputSplit getInputSplit(String scanStart, String scanStop, Pair<byte[][], byte[][]> startEndKeys, int i) {
String start = Bytes.toString(startEndKeys.getFirst()[i]);
String end = Bytes.toString(startEndKeys.getSecond()[i]);
String startSalt;
if (start.length() == 0)
startSalt = "0";
else
startSalt = start.substring(0, 1);
byte[] startRowKey = Bytes.toBytes(startSalt + "-" + scanStart);
byte[] endRowKey = Bytes.toBytes(startSalt + "-" + scanStop);
TableSplit tableSplit;
try {
HRegionLocation regionLocation = regionLocator.getRegionLocation(startEndKeys.getFirst()[i]);
String hostnamePort = regionLocation.getHostnamePort();
tableSplit = new TableSplit(table.getName(), scan, startRowKey, endRowKey, hostnamePort);
} catch (IOException ex) {
throw new HBaseRetrievalException("Problem while trying to find region location for region " + i, ex);
}
return tableSplit;
}
}
So, this creates an InputSplit. I would like to know whether this split is created with correct parameters. How to do that?
If the class is part of a framework, then you shouldn't test it directly, as the framework has tested it for you. If you still want to test the behaviour of this object, look at the cause-reaction this object would cause. More specifically: mock the object, have it do stuff and check if the affected objects (which you can control) carry out the expected behaviour or are in the correct state.
For more details you should probably update your answer with the framework you're using and the class of said framework you wish to test
This is possibly one of those cases where you shouldn't be testing it directly. This object is supposedly USED for something, yes? If it's not created correctly, some part of your code will break, no?
At some point or another, your application depends on this created object to behave in a certain way, so you can test it implicitly by testing that these procedures that depend on it are working correctly.
This can save you from coupling more abstract use cases from the internal workings and types of the framework.
Suppose I have a class with two methods where I don't care which is called...
public class Foo {
public String getProperty(String key) {
return getProperty(key, null);
}
public String getProperty(String key, String defaultValue) {
//...
}
}
Both the below (from another class, still in my application) should pass my test:
public void thisShouldPass(String key) {
// ...
String theValue = foo.getProperty(key, "blah");
// ...
}
public void thisShouldAlsoPass(String key) {
// ...
String theValue = foo.getProperty(key);
if (theValue == null) {
theValue = "blah";
}
// ...
}
I don't care which was called, I just want one of the two variants to be called.
In Mockito, I can generally do things like this:
Mockito.verify(foo, atLeastOnce()).getProperty(anyString());
Or:
Mockito.verify(foo, atLeastOnce()).getProperty(anyString(), anyString());
Is there a native way to say "verify either one or the other occurred at least once"?
Or do I have to do something as crude as:
try {
Mockito.verify(foo, atLeastOnce()).getProperty(anyString());
} catch (AssertionError e) {
Mockito.verify(foo, atLeastOnce()).getProperty(anyString(), anyString());
}
You could use atLeast(0) in combination with ArgumentCaptor:
ArgumentCaptor<String> propertyKeyCaptor = ArgumentCaptor.forClass(String.class);
Mockito.verify(foo, atLeast(0)).getProperty(propertyKeyCaptor.capture(), anyString());
ArgumentCaptor<String> propertyKeyCaptor2 = ArgumentCaptor.forClass(String.class);
Mockito.verify(foo, atLeast(0)).getProperty(propertyKeyCaptor2.capture());
List<String> propertyKeyValues = propertyKeyCaptor.getAllValues();
List<String> propertyKeyValues2 = propertyKeyCaptor2.getAllValues();
assertTrue(!propertyKeyValues.isEmpty() || !propertyKeyValues2.isEmpty()); //JUnit assert -- modify for whatever testing framework you're using
Generally, if you're calling verify on a "getter" of any sort, you're assuming too much about the implementation. Mockito is generally designed for flexible tests (compared to "brittle" test that need to change even if the code is correct); your test should care more about whether the value is correct as opposed to which methods were used to get that value. A better solution might be to stub both getters to return a predictable value, and then use a normal assertion against the same value to ensure it plumbs through to the correct place.
when(mockFoo.getProperty("bar")).thenReturn("bar value");
when(mockFoo.getProperty("bar", anyString())).thenReturn("bar value");
// ...
assertEquals("bar value", new SystemUnderTest(mockFoo).getBarProperty());
Mockito's documentation spells this out:
Although it is possible to verify a stubbed invocation, usually it's just redundant. Let's say you've stubbed foo.bar(). If your code cares what foo.bar() returns then something else breaks (often before even verify() gets executed). If your code doesn't care what get(0) returns then it should not be stubbed.
That said, if this is a pattern you're required to support (or a method call with both overloads and side-effects) you can get a lot of information via Mockito.mockingDetails and MockingDetails.getInvocations, including invocations as of Mockito 1.10.0. You would need to loop through the Invocation objects to check against multiple methods.
boolean found = false;
Method method1 = Foo.class.getMethod("getProperty", String.class);
Method method2 = Foo.class.getMethod("getProperty", String.class, String.class);
for (Invocation invocation : Mockito.mockingDetails(foo).getInvocations()) {
if (method1.equals(invocation.getMethod())
|| method2.equals(invocation.getMethod()) {
found = true;
break;
}
}
assertTrue("getProperty was not invoked", found);
Note that this second solution is a little dangerous, as it does not benefit from automatic refactoring tools built into IDEs, and may be harder to read than some other solutions. (The above may also be missing calls to isIgnoredForVerification, markVerified, and other niceties.) However, if you foresee needing this frequently across a large codebase, then using Mockito's built-in APIs may afford you much more flexibility than you would have otherwise.
In your particular case, getProperty(String) calls getProperty(String, String) internally.
public String getProperty(String key) {
/*
* getProperty(String, String) is called anyway.
* Why not simply verify the occurrence of that?
*/
return getProperty(key, null);
}
Simply verifying the second method would be equivalent to verifying the occurrence of either one or the other at least once.
Mockito.verify(foo, atLeastOnce()).getProperty(anyString(), anyString());
With Deadbolt's module we can check the restrictedResource with a ressource name and parameters in the view.
For example in my view, I have it, and it works well:
#{deadbolt.restrictedResource resourceKeys:['Domain'] , resourceParameters:['domainid':domain.id]}
<li>${domain.title}</li>
#{/deadbolt.restrictedResource}
But in my controller, I just can check the ressource name but I don't find a way to check it in my RestrictedResourcesHandler passing the domainid with.
I am looking for a solution to do something like that:
#RestrictedResource(name = {"Domain"}, params = {domainid})
public static void showDomain(String domainid)
{
}
Thanks in advance
It's not possible to have dynamic information in an annotation, but you can use params to define the name of an incoming value in the request. However, this information isn't passed into the handler at the moment because it expects a map. While you can pass in a map of parameters from the restrictedResource tag, you can't do this from an annotation so an empty map is passed into the handler.
Your best approach here is to pull a well-known parameter name from the request object. I need to have a rethink about the best way to do this without breaking backwards compatibility.
Steve (author of Deadbolt)
I've found a way the solved the problem, not the best I think, but it is the Steve Chaloner's solution (Deadbolt's creator), and it works.
For example, if your Controller's method argument is named "id", and you want to check this id inside your checkAccess method :
// Controller's method :
#RestrictedResource(name = {"Domain"})
public static void showDomain(String id){}
Just check at the beginning of your checkAccess method the Map "resourceParameters" is empty, and use the request object to get the parameters:
public AccessResult checkAccess(List<String> resourceNames,
Map<String, String> resourceParameters)
{
Map<String, String> hashm = new HashMap<String,String>();
if(resourceParameters != null && !resourceParameters.isEmpty()){
hashm = resourceParameters;
}else if(Http.Request.current().routeArgs!= null && !Http.Request.current().routeArgs.isEmpty()){
hashm = Http.Request.current().routeArgs;
}
}
Then just have to foreach your hashmap inside your checkAccess method to get your Controller's method argument and check the Access as you wish.
for (Map.Entry<String,String> mp : hashm.entrySet())
{
// Get the id argument
if(mp.getKey().equals("id"))
{
// Do something with the value..
mp.getValue()
}
}