Order of evaluation of Hamcrest CombinableMatcher - java

I wanted to write a logic for matcher like matcher_1 AND matcher_2 OR matcher_3 and matcher_4 where matcher_i are just placeholder for hamcrest matchers.
I decided to write it using CombinableMatcher like this -
new CombinableMatcher<String>(matcher_1).and(matcher_2).or(matcher_3).and(matcher_4)
Now, the question is in which order matchers will be evaluated ?
Will it be ((((matcher_1) AND matcher_2) OR matcher_3) and matcher_4) or
any other order like ((matcher_1 AND matcher_2) OR (matcher_3 and matcher_4)) ?

Matcher results will be evaluated from left to right. So, it will be ((((matcher_1) AND matcher_2) OR matcher_3) and matcher_4) and not ((matcher_1 AND matcher_2) OR (matcher_3 and matcher_4)).
Following example ran in mockito 3.11 and hamcrest 2.2 proves the above -
#RunWith(MockitoJUnitRunner.class)
public class SomeTest {
#Mock
SomeInterface mock;
#Test
public void test(){
when(mock.complicatedMethod(eq(1), MockitoHamcrest.argThat(
new CombinableMatcher<String>(equalTo("hello"))
.and(containsString("ello"))
.or(CoreMatchers.<String>instanceOf(String.class))
.and(containsString("XYZ"))
) )).thenReturn("done");
System.out.println(mock.complicatedMethod(1 ,"hello"));// Prints null
}
}
interface SomeInterface {
public String complicatedMethod(int i , String str) ;
}
If matcher results which were evaluated in this order ((matcher_1 AND matcher_2) OR (matcher_3 and matcher_4)) [Which is by the way Java precedence order for boolean operator], then "done" would have been printed but it fails and null is printed which means ((((matcher_1) AND matcher_2) OR matcher_3) and matcher_4) is what it is considering.

Good to see that someone understands the idea of composing matchers.
I'm wondering why you're concerned with the ordering of matchers? Ideally it shouldn't matter, since you also don't know how often a matcher will be called.
If you have issues at this level of detail, it might be best to write a custom matcher.

Related

Writing Unittest which takes 2 list

I am a new Junit Learner I would like to test my code according to 2 list class. However I never saw any example of that.
My real code is below :
public static List<JourneyType> applyFilter(List<JourneyType> journeyList, List<AvailabilityFilterOptionType> filterOptions)
{
List<JourneyType> filteredJourneyList = new ArrayList<>();
filteredJourneyList = applyStopFilters(journeyList, filterOptions);
filteredJourneyList = applyCarrierFilters(filteredJourneyList, filterOptions);
filteredJourneyList = applyRbdFilters(filteredJourneyList, filterOptions);
filteredJourneyList = applyDurationFilter(filteredJourneyList, filterOptions);
return filteredJourneyList;
}
and my test scenario :
#Test
public void testApplyFilter()
{
fail("Not yet implemented");
}
Thank you in advanced
Actually, this is pretty simple.
#Test
public void testApplyFilter()
{
assertThat(someObject.applyFilter(journies, filters), is(expectedOutcome));
}
In other words: you know what this method is supposed to do. ( Well, such knowledge is the prerequisite for testing stuff. When you don't know what your code is doing, there is no point in verifying its correctness via testing...)
As in: given some known input data, you should be able put down an expectation about output coming back. And that is what you check for.
Of course, the tricky part could be to correctly identify dependencies, and mock them where needed.
But ideally, your test should just be that: testing the public contract of your method under test. Something goes in, and you check that the output coming back meets your expectations. Ideally, you have to mock nothing for such tests, because you do not at all rely on testing implementation details. You only test the public contract "given this input, this is the expected output".
( where: assertThat() is just a different type of assert, and is() is a hamcrest matcher. There are many other hamcrest matchers, such as containsInAnyOrder() (that one is really neat if you don't care about the order of elements returned, but one has to understand that is used slightly different, it would need containsInAnyOrder(expectedOutcomeList.toArray()) methinks )

PowerMockito verifyNew withArguments of an object and an array of objects

i am trying to test that a method creates an object. i have it almost working using PowerMockito.verifyNew().withArguments() however, the Arguments that are passed to the constructor are an object and an ArrayList of objects. the output of the test is:
Actual
invocationSubstitute.performSubstitutionLogic(
1,
6,
11,
13,
[au.edu.sccs.csp3105.NBookingPlanner.Person#2449cff7],
au.edu.sccs.csp3105.NBookingPlanner.Room#62da83ed,
"description"
);
Expected
invocationSubstitute.performSubstitutionLogic(
1,
6,
11,
13,
[au.edu.sccs.csp3105.NBookingPlanner.Person#40bffbca],
au.edu.sccs.csp3105.NBookingPlanner.Room#42a9a63e,
"description"
);
i can see that the problem is the objects are the same type but not the same object, is there a way of saying the Expected object is of the correct type?
test:
#RunWith(PowerMockRunner.class)
#PrepareForTest({Planner.class, Meeting.class})
public class MonthInput {
Planner planner;
#Rule
public final TextFromStandardInputStream systemInMock = emptyStandardInputStream();
#Rule
public final ExpectedSystemExit exit = ExpectedSystemExit.none();
#SuppressWarnings("deprecation")
#Before
public void setup() throws Exception {
Meeting meetingMock = Mockito.mock(Meeting.class);
PowerMockito.whenNew(Meeting.class).withAnyArguments().thenReturn(meetingMock);
}
#Test
public void MonthInputofless5() throws Exception {
// make spy
planner = Mockito.spy(Planner.class);
//override main menu with do nothing
Mockito.doNothing().when(planner).mainMenu();
//provide inputs
systemInMock.provideLines("1","6","11","13","ML13.218","Mark Colin","done","description");
//set expected outputs
ArrayList<Person> attendees = new ArrayList<Person>();
attendees.add(new Person("Mark Colin"));
Room where = new Room("ML13.218");
//call the method
planner.scheduleMeeting();
//set passing terms
PowerMockito.verifyNew(Meeting.class).withArguments(1,6,11,13,attendees,where,"description");
}
Fix your code
A verify simple fix on your classes: implement hashCode and equals on Person and Room so Powermock verification can actually compare two objects for equality and not just rely on the object reference.
Fix your tests
If you don't want to fix your code, but the test, you could either use a Mockito matcher (i.e. org.mockito.Matchers.eq or org.mockito.Matchers.any). But please note, that eq relies on equals and won't work unless you implement it (see above). But any would match any object of that type (surprise!)
PowerMockito.verifyNew(Meeting.class)
.withArguments(eq(1),eq(6),eq(11),eq(13),
any(List.class),
any(Room.class),
eq("description"));
If the actual value matters, you could use an ArgumentCapture instead of a matcher and check the captured value.
Theoretically, it should look like this:
final ArgumentCaptor<Person> personCaptor = forClass(Person.class);
final ArgumentCaptor<Room> roomCaptor = forClass(Room.class);
PowerMockito.verifyNew(Planner.class)
.withArguments(eq(1),eq(6),eq(11),eq(13),
personCaptor.capture(),
roomCaptor.capture(),
eq("description"));
final Person passedParam = personCaptor.getValue();
//do your comparison here
But I didn't get the capture example running, so maybe that's only possible with plain Mockito.
Just don't!
All that said, you should verify your overall approach. Using Whitebox-Testing may create very brittle tests and won't help much in refactoring and may further foster bad class design.
The imprudent use of Powermockito is an anti-pattern. It's a very mighty tool for testing the untestable, i.e. badly designed legacy code from the dark ages of the internet where the homepages were handcrafted HTMLs and full of wobbly GIFs.
Don't use it for new, greenfield projects. Just don't.
Instead try focusing on the observable result of any action. Invoking the scheduleMeeting() method may produce any result that is more easy to check - it's the result that matters, not the way to get they. Trust me, not a single user gets happier when a constructor was called.
Results could be
an instance of a meeting (as return value, that would be best in your example)
a state change in the planner (any getters?)
a state change in a downstream service / db
a written file
an output written to System.out

Mockito ArgumentMatchers doesNotMatch?

ArgumentMatchers.matches( String regex ) exists... and it is possible to devise regexes which don't match a given String. But it is far from trivial (several threads in SO).
Is it wrong of me (or wrong-headed) to think it might be a nice idea to request the Mockito designers to take the heavy-lifting out of this and add it as a feature? It just seems that, in the context of mocking and so forth, it is a far-from-exceptional use case...
PS also, I'm not clear with ArgumentMatchers.matches how you go about saying "this may be a multiline String we're matching against, don't worry about it"... wouldn't it be better to have a Pattern rather than a simple String?
later
Feature request "enhanced" at Mockito HQ (on Github). "bric3" there says one should use Jeff Bowman's technique for "does not match". But she/he seems to think the Pattern idea is worth thinking about.
Re not(): Mockito's own documentation says "Use additional matchers very judiciously because they may impact readability of a test. It is recommended to use matchers from Matchers and keep stubbing and verification simple."
Also I find I must "possible dupe" my own question: How to write a matcher that is not equal to something. Searching with hindsight is always easier...!
later still
Many thanks to Brice for adding this so quickly. Updated my gradle.build and... new 4.1 core downloaded from Maven Central and immediately available for use.
No need for a request: You can compose what you want using AdditionalMatchers.not.
when(yourComponent.acceptString(not(matches("foo|ba[rz]"))))
.thenThrow(new IllegalArgumentException());
If you want to match a Pattern, you might need to write your own ArgumentMatcher subclass, but it's quite easy from there:
public class MatchesPattern implements ArgumentMatcher<String> {
private final Pattern pattern;
public MatchesPattern(Pattern pattern) { this.pattern = pattern; }
#Override public boolean matches(String string) {
return pattern.matcher(string).matches();
}
#Override public String toString() {
return "[string matching /" + pattern.toString() + "/]";
}
/** Optional. */
public static MatchesPattern matchesPattern(Pattern pattern) {
return new MatchesPattern(pattern);
}
}
You can then consume that class using:
when(yourComponent.acceptString(not(argThat(new MatchesPattern(yourPattern)))
.thenThrow(new IllegalArgumentException());
// or with the static factory method:
when(yourComponent.acceptString(not(argThat(matchesPattern(yourPattern)))
.thenThrow(new IllegalArgumentException());
For future readers, Mockito 2.4.1 has been released with support of the Pattern class :
Now you should be abble to write :
when(yourComponent.acceptString(not(matches(Pattern.compile(...)))
.thenThrow(new IllegalArgumentException());

What is the most efficiant way to check if an exception is thrown for all statements in junit?

I've got a function which throws a ParseException when given an invalid input, and I would like to write a unit test for it. This test is intended to make sure that an exception is thrown on all invalid inputs. (For this question, just assume that an input is defined as valid if it starts with a [, ends with a ], and has no ['s or ]'s anywhere else in it.)
I currently have this test:
#Test
public void invalidListFromatShouldFail() {
final String[] tests = {
"[[]",
"[]]",
"[] hi",
"hi",
"[h]i",
"[hi []",
"[[]]"
};
for(String toTest : tests) {
try {
ListUtil.parseList(toTest, TestEnum.class);
} catch (ParseException e) {
assertThat(e.getMessage(), startsWith(
"§cList format is invalid: It must start with " +
"'[' and end with ']', and not have any '['" +
" or ']' anywhere else in it."));
continue;
}
fail("Exception was not thrown for " + toTest + "!");
}
}
And it works, but it doesn't seem like the proper way. I tried using ExpectedException, but the code still exits immediately. (What I tried was this:
#Test
public void invalidListFromatShouldFail() throws ParseException {
expected.expect(ParseException.class);
expected.expectMessage(startsWith(
"§cList format is invalid: It must start with " +
"'[' and end with ']', and not have any '['" +
" or ']' anywhere else in it."));
ListUtil.parseList("[[]", TestEnum.class);
ListUtil.parseList("[]]", TestEnum.class);
//ECT...
}
but that only tests the first value; if I were to put a fail() at the very end it still would succeed because the exception occurred).
My question is: Is there a more elegant way to write this test, using ExpectedException or some other method?
Use JUnit parameterized tests : https://github.com/junit-team/junit/wiki/Parameterized-tests
As an alternative you can use JUnitParams : https://github.com/Pragmatists/junitparams
Cheers,
Actually, you seem to need two things here. One is writing a test that checks that an exception was thrown and the other is running the same test for multiple inputs.
First, start by parametrizing your test. In this approach your test method is written so that it only runs for a single input, and additional code (usually annotations) make this method run for multiple inputs. This makes your code simpler because you don't have to write the loop or put data in some array or list. You can use JUnit's built-in Parameterized test runner for this (docs and example) but I personally find its syntax cumbersome. My recommendation would be for you to use JUnitParams which has very concise and sweet syntax.
For checking that an exception was thrown in a method that tests a single input, you can use the expected parameter in #Test annotation. An alternative would be using catch-exception library which is nicer if you have longer tests. Here, I think the built-in is good enough.
So, using JUnitParams and the expected annotation parameter, your test could look like this:
#Test(expected = ParseException.class)
#Parameters(
"[[]",
"[]]",
"[] hi",
"hi",
"[h]i",
"[hi []",
"[[]]"
)
public void invalidListFromatShouldFail(String input) {
ListUtil.parseList(toTest, TestEnum.class);
}
This is quite short and readable as well.
EDIT: Here is a better example of using Parameterized, which also mentions #Parameter annotation which allows you to inject values directly into a field without the need to create a constructor in your test class. Still, having used both, I really recommend JUnit Params over Parameterized as the difference in ease of use and test readability is huge.
You need to annotate your test with:
#Test(expected = YourException.class)
here's a good tutorial: http://www.mkyong.com/unittest/junit-4-tutorial-2-expected-exception-test/
also have a look at parametrized tests: https://github.com/junit-team/junit/wiki/Parameterized-tests

Mockito anyMapOf nested generics

I am attempting to verify that a method with the following signature was called:
public void process(Map<String, Set<String>> data) {
...
}
The nested parameterized Set is causing me difficulties. I can get it to verify correctly with the any() matcher like so:
verify(dataProcessor).process(Matchers.<Map<String, Set<String>>> any());
As described in Mockito: Verifying with generic parameters although annoyingly it doesn't work if I do a direct static import of Matchers.any and call it as just:
verify(dataProcessor).process(<Map<String, Set<String>>> any())
But anyMapOf(clazz, clazz) seems the more appropriate matcher in this case. Since you can't do Set.class I'm not sure how you would do this. The following doesn't work because of the lack of generic:
verify(dataProcessor).process(anyMapOf(String.class, Set.class));
Is it possible to verify this situation with anyMapOf or should I stick with Matchers.<>any()?
There's no way to use anyMapOf to do this. It's designed to help with the simple case of mapping simple classes to simple classes in Java 7, and yours is more complex than that.
Java 8 parameter inference improved, so in Java 8, you can just use any().
verify(dataProcessor).process(Matchers.any());
Barring that, the best way to make this look is either like you wrote above:
verify(dataProcessor).process(Matchers.<Map<String, Set<String>>>any());
Or by extracting the matcher to a static function, which gives Java just enough information it needs to infer the type on its own:
#Test public void yourTest() {
// ...
verify(dataProcessor).process(anyStringSetMap());
}
private static Map<String, Set<String>> anyStringSetMap() {
return any();
}
(Caveat: Note that the return value of anyStringSetMap() is null; it's the side-effect of calling any that you're looking for. The extracted method is just to inform the Java compiler of the expected return type; beware that doing anything fancier will probably break in really-quite-interesting ways.)

Categories