Caching variables in a custom Hamcrest Matcher - java

I have created a custom Hamcrest matcher for an interface I'm using.
The matcher is an instance of TypeSafeMatcher and it overrides the following three methods:
TypeSafeMatcher#matchesSafely(T item) : boolean
TypeSafeMatcher#describeMismatchSafely(T item, Description mismatchDescription) : void
TypeSafeMatcher#describeTo(Description description) : void
The class I'm matching handles the validation of a certain type of objects. It comes from an external library so I cannot simply change it. Let's call this class ValidationSubject
Every instance of ValidationSubject this class defines some logic behind the validation to be performed. This is done by implementing ValidationSubject#validate(ValidationData validationData) where validationData is a builder-type object that allows the programmer to report validation errors based on the state of an object of a class implementing ValidationSubject
public class Foo implements ValidationSubject {
private String state;
private Map<String, Baz> moreState;
// constructor, methods affecting the state
// this method is required by ValidationSubject
#Override
public void validate(ValidationData validationData) {
/*
* call methods on validationData based on the state
* of the object
*/
}
}
I'm using my matcher to test the validation logic implemented in each concrete class such as Foo.
In order to do that, I'd need to stub/mock/spy an instance of ValidationData in each test case and see how the state of the ValidationData object changed based on the logic performed by the subject under test. That's a lot of boilerplate. I want my matcher to abstract that away
assertThat(testedValidationSubject, hasValidationErrors("Illegal character in name", "Description exceeds 200 words", "Age cannot be negative"));
In this case, what I'm really matching against the arguments of the hasValidationErrors matcher is a set of String values that the subject under test stored in the ValidationData object.
Extracting these values takes a bit of code.
return new TypeSafeMatcher<ValidationSubject>() {
#Override
protected boolean matchesSafely(ValidationSubject item) {
// this calls the relevant methods on 'item' internally
Validator validator = new Validator(item);
List<ValidationMessage> errorMessages = validator.getErrorMessageGroup()
.getMessages();
Set<String> actualMessages = errorMessages.stream().map(e -> e.getMessage())
.collect(Collectors.toSet());
Set<String> expectedMessages = Stream.of(expectedErrors).collect(Collectors.toSet());
Set<String> missingMessages = SetUtils.difference(expectedMessages, actualMessages);
Set<String> unexpectedMessages = SetUtils.difference(actualMessages, expectedMessages);
return SetUtils.union(unexpectedMessages, missingMessages).isEmpty();
}
#Override
public void describeMismatchSafely(final ValidationSubject item, final Description description) {
// this calls the relevant methods on 'item' internally
Validator validator = new Validator(item);
List<ValidationMessage> errorMessages = validator.getErrorMessageGroup()
.getMessages();
Set<String> actualMessages = errorMessages.stream().map(e -> e.getMessage())
.collect(Collectors.toSet());
Set<String> expectedMessages = Stream.of(expectedErrors).collect(Collectors.toSet());
Set<String> missingMessages = SetUtils.difference(expectedMessages, actualMessages);
Set<String> unexpectedMessages = SetUtils.difference(actualMessages, expectedMessages);
description.appendText("Validation errors were missing or unexpected\n")
.appendValueList("\tSupefluous messages: ", ", ", "\n", unexpectedMessages.toArray())
.appendValueList("\tMissing messages: ", ", ", "\n", missingMessages.toArray());
}
#Override
public void describeTo(Description description) {
description.appendText("validation should result in the expected errors");
}
}
This piece of code is repeated line-by-line:
Validator validator = new Validator(item);
List<ValidationMessage> errorMessages = validator.getErrorMessageGroup()
.getMessages();
Set<String> actualMessages = errorMessages.stream().map(e -> e.getMessage())
.collect(Collectors.toSet());
Set<String> expectedMessages = Stream.of(expectedErrors).collect(Collectors.toSet());
Set<String> missingMessages = SetUtils.difference(expectedMessages, actualMessages);
Set<String> unexpectedMessages = SetUtils.difference(actualMessages, expectedMessages);
I can get rid of the duplication by wrapping this piece in a method or a lambda expression (returning a pair of sets or accepting as a parameter a function to compute the boolean or string I need) but ideally, I'd like to only execute this once.
I need the item to figure out the result of both matchesSafely and the message output by describemisMatchSafely but each time it's passed as a parameter. It's not a parameter of the static method hasValidationErrors so I can't see a clean way to cache the result in a couple of variables.
I could potentially execute this code in one of those methods and cache it in a field but the Javadoc for TypeSafeMatcher seems to make no guarantees as to which method is executed first.

If I understand what you're trying to do, you're looking for functionality provided by TypeSafeDiagnosingMatcher. Try extending that instead of TypeSafeMatcher:
return new TypeSafeDiagnosingMatcher<ValidationSubject>() {
#Override
protected boolean matchesSafely(ValidationSubject item, Description mismatchDescription) {
// this calls the relevant methods on 'item' internally
Validator validator = new Validator(item);
List<ValidationMessage> errorMessages = validator.getErrorMessageGroup()
.getMessages();
Set<String> actualMessages = errorMessages.stream().map(e -> e.getMessage())
.collect(Collectors.toSet());
Set<String> expectedMessages = Stream.of(expectedErrors).collect(Collectors.toSet());
Set<String> missingMessages = SetUtils.difference(expectedMessages, actualMessages);
Set<String> unexpectedMessages = SetUtils.difference(actualMessages, expectedMessages);
mismatchDescription.appendText("Validation errors were missing or unexpected\n")
.appendValueList("\tSuperfluous messages: ", ", ", "\n", unexpectedMessages.toArray())
.appendValueList("\tMissing messages: ", ", ", "\n", missingMessages.toArray());
return SetUtils.union(unexpectedMessages, missingMessages).isEmpty();
}
#Override
public void describeTo(Description description) {
description.appendText("validation should result in the expected errors");
}
}

Related

Mockito and matching varargs properly with overloaded methods with Spring Redis

I have following method in my service.
private final ReactiveRedisTemplate<String, String> reactiveRedisTemplate;
public Mono<Boolean> deleteObject(String... keys) {
return reactiveRedisTemplate.delete(keys).map(c -> c > 0);
}
ReactiveRedisTemplate has two internal implementations for delete with different signatures.
#SafeVarargs
public final Mono<Long> delete(K... keys) {
Assert.notNull(keys, "Keys must not be null!");
Assert.notEmpty(keys, "Keys must not be empty!");
Assert.noNullElements(keys, "Keys must not contain null elements!");
if (keys.length == 1) {
...
public Mono<Long> delete(Publisher<K> keys) {
Assert.notNull(keys, "Keys must not be null!");
return this.doCreateFlux((connection) -> {
Now, when I have the following
var key = "key";
when(reactiveRedisTemplate.delete(key)).thenReturn(Mono.just(1L));
StepVerifier.create(service.removeObject(key))
.expectNextMatches(c -> c)
.verifyComplete();
verify(this.reactiveRedisTemplate, times(1)).delete(key);
I'm getting error NullPointerException, so it looks like the varargs doesn't match. With ArgumentMatcher, following is received:
when(reactiveRedisTemplate.delete(ArgumentMatchers.<String>any())).thenReturn(Mono.just(1L));
=> java.lang.IllegalArgumentException: Keys must not contain null elements!
when(reactiveRedisTemplate.delete(eq(new String[] { key }))).thenReturn(Mono.just(1L));
=> java.lang.IllegalArgumentException: Keys must not be null!
when(reactiveRedisTemplate.delete(anyString())).thenReturn(Mono.just(1L));
=> java.lang.NullPointerException (doesn't seem to match)
lastly a custom matcher:
public class StringVarargsMatcher implements VarargMatcher, ArgumentMatcher<String[]> {
private String[] expectedValues;
public StringVarargsMatcher(String... expectedValues) {
Arrays.sort(expectedValues);
this.expectedValues = expectedValues;
}
#Override
public boolean matches(String[] strings) {
Arrays.sort(strings);
return Arrays.equals(strings, expectedValues);
}
}
when(reactiveRedisTemplate.delete(argThat(new StringVarargsMatcher(key)))).thenReturn(Mono.just(1L));
=> java.lang.IllegalArgumentException: Keys must not be null!
Is there any way to overcome this so I could mock delete from ReactiveRedisTemplate and get a unit test done here?
You are not mocking public final Mono<Long> delete(K... keys) as it is a final method and you are not using inline mock maker (as discussed in comments).
As a consequence, real method is called.
ArgumentMatcher factory methods, such as any()
register a metcher in Mockitos internal state
return null
Combine that with a real method being called - you get errors on assertions made by the real method.
See: Mocking final types, enums and final methods

Archunit test to not allow to log the return value of method with specific annotation using Log4j logging methods

Suppose I have below Person class with one getAccountNumber() method having #ShouldNotBeLogged custom annotation.
public class Person {
private String name;
private String accountNumber;
#ShouldNotBeLogged
public String getAccountNumber() {
return accountNumber;
}
}
So, the question is I want to create a archunit which checks that any class (like HelloWorld class below here) having org.apache.logging.log4j.Logger type static field cannot log the return value of method with #ShouldNotBeLogged annotation.
Here, archunit test should report violation in HelloWorld.logMessage method - as it is logging the return value of Person class object - getAccountNumber() method having #ShouldNotBeLogged custom annotation.
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class HelloWorld {
private static final Logger logger = LogManager.getLogger(HelloWorld.class);
public void logMessage(Person person) {
logger.debug("logging Acc No. - {}", person.getAccountNumber());
}
}
ArchUnit (as of the current version 0.23.1) does not analyze parameter values of a method call – but maybe it's good enough to find calls to a logger and to a #ShouldNotBeLogged annotated method in the same line?
False positives (unrelated calls in the same line) like
logger.debug("This is okay:"); String accountNumber = person.getAccountNumber();
might already be prevented by code style rules.
For false negatives (the return value of a #ShouldNotBeLogged annotated method is logged, but the method calls are spread over multiple lines) like
logger.debug("This is not okay: {}",
person.getAccountNumber()
);
, you could easily adapt the rule to require that the method calls
have some reasonable™ line number distance (which unfortunately increases the probability of false positives).
Yes, this just a heuristic. But it might be better than nothing. 🤷‍♂️
#ArchTest
ArchRule rule = noClasses().should(new ArchCondition<JavaClass>("log return values of #ShouldNotBeLogged methods") {
#Override
public void check(JavaClass javaClass, ConditionEvents events) {
Set<JavaMethodCall> loggerCalls = new HashSet<>();
Set<JavaMethodCall> shouldNotBeLoggedCalls = new HashSet<>();
javaClass.getMethodCallsFromSelf().forEach(methodCall -> {
Optional<JavaMethod> resolvedTarget = methodCall.getTarget().resolveMember();
if (resolvedTarget.isPresent()) {
JavaMethod javaMethod = resolvedTarget.get();
if (javaMethod.getOwner().isAssignableTo(Logger.class)) {
loggerCalls.add(methodCall);
}
if (javaMethod.tryGetAnnotationOfType(ShouldNotBeLogged.class).isPresent()) {
shouldNotBeLoggedCalls.add(methodCall);
}
}
});
loggerCalls.forEach(loggerCall ->
shouldNotBeLoggedCalls.stream()
.filter(shouldNotBeLoggedCall ->
shouldNotBeLoggedCall.getLineNumber() == loggerCall.getLineNumber()
)
.forEach(shouldNotBeLoggedCall -> {
String message = loggerCall.getOrigin().getDescription() + " logs in the same line as "
+ shouldNotBeLoggedCall.getTarget().getDescription() + " is called "
+ shouldNotBeLoggedCall.getSourceCodeLocation();
events.add(SimpleConditionEvent.satisfied(javaClass, message));
})
);
}
});
For your example this rule fails with
Architecture Violation [Priority: MEDIUM] - Rule 'no classes should log return values of #ShouldNotBeLogged methods' was violated (1 times):
Method <HelloWorld.logMessage(Person)> logs in the same line as method <Person.getAccountNumber()> is called (HelloWorld.java:9)

How to implement a checked builder pattern in Java

I'm trying to implement a checked builder pattern similar to how it's described in this:
https://dev.to/schreiber_chris/creating-complex-objects-using-checked-builder-pattern
The result I'm trying to reach is as follows:
Builder builder = new Builder('TestVal')
.when('this').then(new Set<String> {'val1','val2'})
.when('that').then(new Set<String> {'val3','val4'});
And the resulting object would contain a collection with any number of whens with the associted thens
e.g. a Map like this (the param for when() is unique):
'this' => ['val1','val2'],
'that' => ['val3','val4']
I'm struggling with a couple things:
How to associate the values passed into then() with the value
passed into when()
How to require then() be called after
when(). (e.g. - .when('this').when('that') //invalid
The easiest way is to use multiple interfaces to enforce your call ordering and then use that knowledge to associate your items. For example, something along these lines:
interface Then{
When then(Set<String> values);
}
interface When{
Then when(String condition);
}
class Builder implements When, Then{
public static When create(){ return new Builder(); }
private Map<String, Set<String>> storedMappings = new HashMap<>();
private String currentCondition;
private Builder(){ }
public Then when(String condition){
currentCondition = condition;
return this;
}
public When then(Set<String> values){
storedMappings.put(currentCondition, values);
return this;
}
}

How to validate that a method annotation is using an attribute with an specific value using archunit

I have an #Audit annotation, it has many optional attributes, I need to enforce the use of one boolean attribute useAccount = true for certain packages.
I am trying to use archunit to accomplish this validation, that way whenever a developer commits code that breaks the rule the CI will break and inform the team.
This would break the build:
#Audit
public myMethod(...) {
...
}
This is the right way:
#Audit(useAccount = true)
public myMethod(...) {
...
}
The problem is that Archunit doesn't currently support asserting over methods. I was expecting to do something like:
methods().that().resideInAnyPackage("..controllers..", "..service..").and().areAnnotatedWith(Audit.class).should(attributeCheckCondition)
Then my custom condition attributeCheckCondition would take care of looking into the attribute value.
Is there a way of retrieving methods as we retrieve classes? Without having to write a more complicated predicate and condition?
Update
Since ArchUnit 0.10.0 it is possible to create rules for members.
methods().that()
.areDeclaredInClassesThat()
.resideInAnyPackage("..controllers..", "..service..")
.and()
.areAnnotatedWith(Audit.class)
.should(attributeCheckCondition)
See also Composing Member Rules in the User Guide.
Original Answer
Since there are currently no basic rule definitions available for methods, an intermediate step is necessary. ArchUnit has a ClassesTransformer to transform JavaClasses into a collection of other types.
ClassesTransformer<JavaMethod> methods = new AbstractClassesTransformer<JavaMethod>("methods") {
#Override
public Iterable<JavaMethod> doTransform(JavaClasses javaClasses) {
Set<JavaMethod> allMethods = new HashSet<>();
for (JavaClass javaClass : javaClasses) {
allMethods.addAll(javaClass.getMethods());
}
return allMethods;
}
};
This ClassesTransformer can then be used as a base for custom rule definitions.
ArchRule rule = ArchRuleDefinition.all(methods)
.that(owner(resideInAnyPackage("..controllers..", "..service..")))
.and(annotatedWith(Audit.class))
.should(haveAttributeValue());
rule.check(javaClasses);
See also Rules with Custom Concepts in the User Guide and this issue.
I found a way of doing it with custom predicate and condition over classes, when I did that I was not aware of Roland's response which seems to be better, as it provides a way to express the rule assertion from the methods perspective which is why I was asking for.
However I wanted to post the solution here so it can be useful for others.
DescribedPredicate<JavaClass> HAVE_A_METHOD_ANNOTATED_WITH_AUDIT =
new DescribedPredicate<JavaClass>("have a method annotated with #Audit")
{
#Override
public boolean apply(JavaClass input)
{
return input.getMethods().stream().anyMatch(method -> method.isAnnotatedWith(Audit.class));
}
};
ArchCondition<JavaClass> ONLY_SET_ATTRIBUTE_USE_ACCOUNT_SET_TO_TRUE =
new ArchCondition<JavaClass>("only set useAccount attribute to true")
{
#Override
public void check(JavaClass item, ConditionEvents events)
{
item.getMethods().stream().filter(method ->
method.isAnnotatedWith(Audit.class) && !method.getAnnotationOfType(Audit.class)
.useAccount()
)
.forEach(method -> {
String message = String.format(
"Method %s is annotated with #Audit but useAccount is not set to true",
method.getFullName());
events.add(SimpleConditionEvent.violated(method, message));
});
}
};
Then the rule is expressed as:
ArchRule ANNOTATION_RULE = classes()
.that()
.resideInAnyPackage("..controller..", "..service..")
.and(HAVE_A_METHOD_ANNOTATED_WITH_AUDIT)
.should(ONLY_SET_ATTRIBUTE_USE_ACCOUNT_SET_TO_TRUE);
Here is another custom example in addition to #raspacorp (who inspired me!).
To check #Secured(ROLE) method annotation, I've implemented the following rule:
public static class SecuredByRoleArchCondition extends ArchCondition<JavaMethod> {
private final String[] expectedRoles;
public SecuredByRoleArchCondition(String[] expectedRoles) {
super(String.format("accessed by #Secured methods with roles %s", Arrays.toString(expectedRoles)));
this.expectedRoles = expectedRoles;
}
public static SecuredByRoleArchCondition haveSecuredAnnotationWithRoles(String... expectedRoles) {
return new SecuredByRoleArchCondition(expectedRoles);
}
#Override
public void check(JavaMethod javaMethod, ConditionEvents events) {
if (!javaMethod.isAnnotatedWith(Secured.class)) {
String message = String.format("Method %s annotation #Secured(%s) is missing",
javaMethod.getFullName(), Arrays.toString(expectedRoles));
events.add(SimpleConditionEvent.violated(javaMethod, message));
return;
}
String[] annotationRoleValues = javaMethod.getAnnotationOfType(Secured.class).value();
if (!Arrays.equals(annotationRoleValues, expectedRoles)) {
String message = String.format("Method %s #Secured with %s has wrong roles, expected %s instead",
javaMethod.getFullName(), Arrays.toString(annotationRoleValues), Arrays.toString(expectedRoles));
events.add(SimpleConditionEvent.violated(javaMethod, message));
}
}
}
Here is a sample usage of this archCondition:
#ArchTest
static ArchRule admin_actions_with_post_mapping_should_be_secured_by_ADMIN_WRITE_role =
methods()
.that().areDeclaredInClassesThat().resideInAnyPackage(ADMIN_PACKAGES)
.and().areAnnotatedWith(PostMapping.class)
.should(haveSecuredAnnotationWithRoles("ADMIN_WRITE"));

Java - how to analyze a function code

We are working with mvc design pattern, where all the data is stored under map.
I want to iterate over all the classes in the system and for each to check what the method is putting on the map and what does the method get from the map.
For example for the next code:
private void myFunc()
{
Object obj = model.get("mykey");
Object obj2 = model.get("mykey2");
.....
model.put("mykey3", "aaa");
}
I want to know that in this function we have 2 gets: mykey and mykey2 and 1 put: mykey3
How can I do it with the code.
Thanks.
You tagged this with "reflection", but that will not work. Reflection only allows you to inspect "signatures". You can use it to identify the methods of a class, and the arguments of the methods.
It absolutely doesn't help you to identify what each method is doing.
In order to find out about that, you would need to either parse the java source code side, or byte code classes. As in: write code that reads that content, and understands "enough" of it to find such places. Which is a very challenging effort. And of course: it is very easy to bypass all such "scanner" code, by doing things such as:
List<String> keysToUpdate = Arrays.asList("key1", "key2");
for (String key : keysToUpdate) {
... does something about each key
Bang. How would you ever write code that reliable finds the keys for that?
When you found that code, now imagine that the list isn't instantiated there, but far away, and past as argument? When you figured how to solve that, now consider code that uses reflection to acquire the model object, and calls method on that. See? For any "scanner" that you write down, there will be ways to make that fail.
Thus the real answer is that you are already going down the wrong rabbit hole:
You should never have written:
Object obj = model.get("mykey");
but something like
Object obj = model.get(SOME_CONSTANT_FOR_KEY_X);
Meaning: there is no good way to control such stuff. The best you can do is to make sure that all keys are constants, coming from a central place. Because then you can at least go in, and for each key in that list of constants, you can have your IDE tell you about their usage.
NOTES
I assumed that your situation is complicated enough that simple or advanced text search in codebase doesn't help you.
This is a hack not a generic solution, designed only for testing and diagnosis purposes.
To use this hack, you must be able to change your code and replace the actual model with the proxy instance while you're testing/diagnosing. If you can't do this, then you have to use an even more advanced hack, i.e. byte-code engineering with BCEL, ASM, etc.
Dynamic proxies have drawbacks on code performance, therefore not an ideal choice for production mode.
Using map for storing model is not a good idea. Instead a well-defined type system, i.e. Java classes, should be used.
A general design pattern for a problem like this is proxy. An intermediate object between your actual model and the caller that can intercept the calls, collect statistics, or even interfere with the original call. The proxied model ultimately sends everything to the actual model.
An obvious proxy is to simply wrap the actual model into another map, e.g.
public class MapProxy<K, V> implements Map<K, V> {
public MapProxy(final Map<K, V> actual) {
}
// implement ALL methods and redirect them to the actual model
}
Now, reflection doesn't help you with this directly, but can help with implementing a proxy faster using dynamic proxies (Dynamic Proxy Classes), e.g.
#SuppressWarnings("unchecked")
private Map<String, Object> proxy(final Map<String, Object> model) {
final InvocationHandler handler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Collect usage stats or intervene
return method.invoke(model, args);
}
};
return (Map<String, Object>) Proxy.newProxyInstance(Map.class.getClassLoader(),
new Class<?>[] { Map.class }, handler);
}
NOTE: Either case you need to be able to replace the actual model with the proxied model at least for the duration of your test.
With another trick, you can find out who called which method of your model. Simply by accessing Thread.currentThread().getStackTrace() and retrieving the appropriate element.
Now puting all the pieces together:
InvocationLog.java
public final class InvocationLog {
private Method method;
private Object[] arguments;
private StackTraceElement caller;
public InvocationLog(Method method, Object[] arguments, StackTraceElement caller) {
this.method = method;
this.arguments = arguments;
this.caller = caller;
}
public Method getMethod() { return this.method; }
public Object[] getArguments() { return this.arguments; }
public StackTraceElement getCaller() { return this.caller; }
#Override
public String toString() {
return String.format("%s (%s): %s",
method == null ? "<init>" : method.getName(),
arguments == null ? "" : Arrays.toString(arguments),
caller == null ? "" : caller.toString());
}
}
ModelWatch.java
public final class ModelWatch {
private final Map<String, Object> modelProxy;
private final List<InvocationLog> logs = new ArrayList<>();
public ModelWatch(final Map<String, Object> model) {
modelProxy = proxy(model);
}
#SuppressWarnings("unchecked")
private Map<String, Object> proxy(final Map<String, Object> model) {
final InvocationHandler handler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method, args, Thread.currentThread().getStackTrace());
return method.invoke(model, args);
}
};
return (Map<String, Object>) Proxy.newProxyInstance(Map.class.getClassLoader(),
new Class<?>[] { Map.class }, handler);
}
private void log(Method method, Object[] arguments, StackTraceElement[] stack) {
logs.add(new InvocationLog(method, arguments, stack[3]));
// 0: Thread.getStackTrace
// 1: InvocationHandler.invoke
// 2: <Proxy>
// 3: <Caller>
}
public Map<String, Object> getModelProxy() { return modelProxy; }
public List<InvocationLog> getLogs() { return logs; }
}
To put it in use:
private Map<String, Object> actualModel = new HashMap<String, Object>();
private ModelWatch modelWatch = new ModelWatch(model);
private Map<String, Object> model = modelWatch.getModelProxy();
// Calls to model ...
modelWatch.getLogs() // Retrieve model activity

Categories