When describing failed verification, by default Mockito prints only call sites where interactions happened. Like this:
Wanted but not invoked:
proxyListener.foundTemplateParam(
"fooBar2",
isNull(),
isNull()
);
-> at foo.ProxyHandlerTest.testThatImplicitParamsScannedCorrectly(ProxyHandlerTest.java:136)
However, there were other interactions with this mock:
-> at foo.ProxyHandler.<init>(ProxyHandler.java:99)
-> at foo.ProxyHandler.<init> (ProxyHandler.java:100)
-> at foo.ProxyHandler.scanForParamSetters(ProxyHandler.java:222)
-> at foo.ProxyHandler.<init>(ProxyHandler.java:102)
-> at foo.ProxyHandler.<init>(ProxyHandler.java:104)
That's useful information but I would also like to see what arguments where passed during those interactions. Is there a way to achieve this?
P. S. I know about mocking with withSettings().verboseLogging(). But it's too verbose and is printed to stdout instead of adding this info to assertion error message.
Update:
Mockito 1.9.0 doesn't support customization of exception error messages out of the box (I just checked their sources).
the safest place to do that is an answer that prints arguments and return the given value.
You could then write something like :
given(some.callWith(arg1, arg2)).will(printArgsAndReturn("some value"));
where printArgsAndReturn("some value") actually returns your custom answer.
Related
Sometimes I want to peek what's the value that is flowing through the steam.
I cannot attach a debugger from my IDE. Because I will see unresolved objects instead of values. If I try to .await().indefinetely() it will raise an exception.
So I'm looking for something like in java streams I can simply use peek(e -> System.out.println(e)) which will simply print the value.
I have something like below
public Uni<TenantDraft> getTenantById(#PathParam("tenantKey") String tenantKey) {
return tenantService.findByTenantKey(tenantKey)
.onItem().ifNotNull().transform(TenantMapper.INSTANCE::tenantToTenantDraft)
.onItem().ifNull().failWith(ForbiddenException::new);
}
You can either use:
.log() which will log all the event
.invoke(item -> System.out.println(item))
Currently I have a test which tries to check a particular exception which looks like this:
assertThatExceptionOfType(DataIntegrityViolationException.class).isThrownBy(
() -> xrepo.save(abc))
.withCauseExactlyInstanceOf(ConstraintViolationException.class);
The exception ConstraintViolationException has a field constraintName available via getter getConstraintName() but I haven't found a way to check that via assertj.
I could imagine something like the following:
assertThatExceptionOfType(DataIntegrityViolationException.class).isThrownBy(
() -> xrepo.save(abc))
.withCauseExactlyInstanceOf(ConstraintViolationException.class)
.with("getConstraintName").isEqualTo("XXXX");
or is there a different way to accomplish this?
withCauseExactlyInstanceOf does not change the object under test, but with havingCause() further assertions can be performed on the cause.
Combined with asInstanceOf() and returns(), a type-safe check would be:
assertThatExceptionOfType(DataIntegrityViolationException.class).isThrownBy(
() -> xrepo.save(abc))
.havingCause()
.asInstanceOf(type(ConstraintViolationException.class))
.returns("XXXX", from(ConstraintViolationException::getConstraintName));
Or without type safety, using isInstanceOf and hasFieldOrPropertyWithValue:
assertThatExceptionOfType(DataIntegrityViolationException.class).isThrownBy(
() -> xrepo.save(abc))
.havingCause()
.isInstanceOf(ConstraintViolationException.class)
.hasFieldOrPropertyWithValue("getConstraintName", "XXX")
May be:
.extracting(x -> ((ConstraintViolationException)x).getConstraintName())
.isEqualTo("XXXX");
The solution of #Eugene brought me to the direction:
assertThatExceptionOfType(DataIntegrityViolationException.class).isThrownBy(
() -> xyrepository.save(xxx))
.withCauseExactlyInstanceOf(ConstraintViolationException.class)
.extracting(s -> ((ConstraintViolationException) (s.getCause())).getConstraintName())
.isEqualTo("XXXX");
The solution of #StefanoCordio looks also fine...
I'm writing some unit test with Mockito and I have the following situation:
AmazonS3 mockedS3 = Mockito.mock( AmazonS3.class )
Bucket mockBucket = Mockito.mock( Bucket.class )
Mockito.when( mockBucket.getName() ).thenReturn("bucket-1-pub")
Mockito.when(mockedS3.doesBucketExistV2("bucket-1-pub")).thenReturn(false)
// here I'm setting up a mock that accepts any CreateBucketRequest
Mockito.when( mockedS3.createBucket(any(CreateBucketRequest.class)) )
.thenReturn( mockBucket )
// execute the code under test
String result = doSomething()
// this is the line of the error where it doesn't see a call to createBucket
// for any CreateBucketRequest
Mockito.verify( mockedS3 ).createBucket( any(CreateBucketRequest.class) )
Here is the error I get:
Wanted but not invoked:
amazonS3.createBucket(
<any com.amazonaws.services.s3.model.CreateBucketRequest>
);
-> at com.amazonaws.services.s3.AmazonS3$createBucket$3.call(Unknown Source)
However, there were exactly 2 interactions with this mock:
amazonS3.doesBucketExistV2("bucket-1-pub");
-> at com.fuseanalytics.archiver.model.dao.AmazonFileDao.checkBucket(AmazonFileDao.java:142)
amazonS3.createBucket(
com.amazonaws.services.s3.model.CreateBucketRequest#255e5e2e
);
-> at com.fuseanalytics.archiver.model.dao.AmazonFileDao.checkBucket(AmazonFileDao.java:147)
Wanted but not invoked:
amazonS3.createBucket(
<any com.amazonaws.services.s3.model.CreateBucketRequest>
);
-> at com.amazonaws.services.s3.AmazonS3$createBucket$3.call(Unknown Source)
However, there were exactly 2 interactions with this mock:
amazonS3.doesBucketExistV2("bucket-1-pub");
-> at com.fuseanalytics.archiver.model.dao.AmazonFileDao.checkBucket(AmazonFileDao.java:142)
amazonS3.createBucket(
com.amazonaws.services.s3.model.CreateBucketRequest#255e5e2e
);
-> at com.fuseanalytics.archiver.model.dao.AmazonFileDao.checkBucket(AmazonFileDao.java:147)
So from tracing out the call my amazonS3.createBucket() call is being called with a CreateBucketRequest, but instead of returning the mockBucket that I have configured. It just returns null, then when I verify it was called that call doesn't match the conditions of verify and blamo exception city.
Update: Things I've tried have been to move the when() calls so they align with the order in which the methods will be called in the code. Didn't work. (I glad that didn't work because how awful would that be?! So much for black box testing right?) I also tried to remove the verify() call for createBucket(). That just resulted in the next verify (not pictured) to blow up because createBucket() still didn't return what I configured.
I can't see how this isn't working so I need some help to see my mistake.
Ok so I left out an important detail my apologies to those who commented. I'm writing my test with Groovy. And it turns out it's Groovy's fault. I started to suspect that Groovy was adding it's Groovy methods to Objects and that might make them appear not the same to Mockito. So while it says it had an interaction with createBucket(CreateBucketRequest). It wasn't recognizing CreateBucketRequest and CreateBucketRequest + Groovy Default Methods as the same thing when using any() matcher.
Now why this is happening is for the Mockito devs to figure out. I suppose I should just go use Spock instead of junit, but that is for another day. Thanks for the help.
I'm using structured logging in a Spring Boot app using logstash and sometimes I want to include key values in the log that I don't want to be used in the message of the log. Is there a StructuredArgument or similar that allows for this?
An example of what I am doing currently is something like this:
// import lombok.extern.slf4j.Slf4j;
// import static net.logstash.logback.argument.StructuredArguments.kv;
// import static net.logstash.logback.argument.StructuredArguments.v;
log.info(
"My message with one arg {}",
v("key1":"arg I want to include value in place of placeholder in message and in json as key/value"),
kv("key2", "arg I only want in the json and not in the message"))
Everything works as I intended, by which I mean the log includes both key value pairs and the message only includes the first value in place of the placeholder. The issue is that I get a warning from the compiler which is flagged by intellij (PlaceholderCountMatchesArgumentCount) about the second structured argument and I would like to avoid this without resorting to suppressing/ignoring it
You can use Markers and pass it before your logging message - more details on github.
logger.info(append("key2", "only json"),
"My message with one arg {}",
v("key1":"arg in msg and json"));
I personally don't like this because markers have different purpose, so if structured argument works for you, just ignore warning in IDE.
Anyway, all this json/structured implementations are workarounds for SLF4J 1.*, which has not built for that. There was SLF4J 2.0.0-alpha1 release almost a yeah ago, but it is still in alpha and I haven't used it. But it's API should be ready for key-values that are crusial in nowadays distributed log management systems.
You can make the log message as a constant String, then the code quality checks will not warn this
You can make the structured argument print nothing into the formatted message:
(1) include the second placeholder {} inside the message
(2) use keyValue() instead of kv()
(3) provide the optional messageFormatPattern parameter (JavaDoc) equal to ""
Adjusting your example:
log.info(
"My message with one arg {}{}", //note (1)
v("key1":"arg I want to include value in place of placeholder in message and in json as key/value"),
keyValue("key2", "arg I only want in the json and not in the message", "")) //note (2) + (3)
This will effectively replace the second placeholder with an empty string.
Sorry for some kind of theoretical question, but I'd like to find a way of quick reading someone else's functional code, building chain of methods use templates.
For example:
Case 1.
When I see use of .peek method or .wireTap from Spring Integration, I primarily expect logging, triggering monitoring or just transitional running external action, for instance:
.peek(params ->
log.info("creating cache configuration {} for key class \"{}\" and value class \"{}\"",
params.getName(), params.getKeyClass(), params.getValueClass()))
or
.peek(p ->
Try.run(() -> cacheService.cacheProfile(p))
.onFailure(ex ->
log.warn("Unable to cache profile: {}", ex.toString())))
or
.wireTap(sf -> sf.handle(msg -> {
monitoring.profileRequestsReceived();
log.trace("Client info request(s) received: {}", msg);
Case 2.
When I see use of .map method or .transform from Spring Integration, I understand that I'm up to get result of someFunction(input), for instance:
.map(e -> GenerateTokenRs.builder().token(e.getKey()).phoneNum(e.getValue()).build())
or
.transform(Message.class, msg -> {
ErrorResponse response = (ErrorResponse) msg.getPayload();
MessageBuilder builder = some tranforming;
return builder.build();
})
Current case.
But I don't have such a common view to .flatMap method.
Would you give me your opinion about this, please?
Add 1:
To Turamarth: I know the difference between .map and .flatMap methods. I actively use both .map, and .flatMap in my code.
But I ask community for theirs experience and coding templates.
It always helps to study the signature/javadoc of the streamish methods to understand them:
The flatMap() operation has the effect of applying a one-to-many transformation to the elements of the stream, and then flattening the resulting elements into a new stream.
So, typical code I expect, or wrote myself:
return someMap.values().stream().flatMap(Collection::stream)
The values of that map are sets, and I want to pull the entries of all these sets into a single stream for further processing here.
In other words: it is about "pulling out things", and getting them into a stream/collection for further processing.
I've found one more use template for .flatMap.
Let's have a look at the following code:
String s = valuesFromDb
.map(v -> v.get(k))
.getOrElse("0");
where Option<Map<String, String>> valuesFromDb = Option.of(.....).
If there's an entry k=null in the map, then we'll get null as a result of code above.
But we'd like to have "0" in this case as well.
So let's add .flatMap:
String s = valuesFromDb
.map(v -> v.get(k))
.flatMap(Option::of)
.getOrElse("0");
Regardless of having null as map's value we will get "0".