boolean matches(Object candidate)
Because matchers evaluate into a boolean values, the logic of our matchers can easily be combined (specification pattern). By chaining matchers, we are capable of composing complex business logic, using our relatively simple matchers as building blocks.
boolean isFastRedCar = allOf(isFast(), isRed(), isCar()).matches(car);
Matchers can be used in many situations, these include:
- Validating the status of an object
- Selecting objects from a collection
Validation
We can use matchers to validate the status of an object. One logical application would be the writing of test-case assertions.
Image a service that retrieves every expired contract from the database. In order to test that service, we would assert that each returned contract has been expired.
contract.expirationDate < currentDate
With hamcrest this goes as follows:
ContractReadService contracts = ...;
assertThat(contracts.allExpired(), everyItem(hasProperty("expirationDate", lessThan(currentDate))));
Which is allot more intuitive than:
ContractReadService contracts = ...;
for(Contract expiredContract : contracts.allExpired()) {
assertTrue(expiredContract.getExpirationDate() < currentDate);
}
By using matchers, our assertions become allot more intuitive. The assertion looks much like a standard english sentence, making the code easier to understand, even without a programming background. In addition, matchers from our domain can be reused for testing. Promoting code reuse and simplifying our test-oriented code base.
Besides the simplification of testing, matchers can also be used to implement core domain functionality.
public class Person {
...
public void move() {
if(tired().matches(this)) {
walk();
} else {
sprint();
}
}
...
}
Because the logic of “is tired” gets modelled as matcher implementation, and not as private method in person, it can easily be reused.
Selection
Besides validation, we can also use matchers to select objects from a collection. Imagine a collection of persons, but we only want to execute logic on the adult persons.
Collectionpersons = ...;
public CollectionadultPersons() {
Collectionadults = new ArrayList ();
MatcherisAdult = hasProperty("age", greaterThanOrEqualTo(18));
for(Person person : persons) {
if(isAdult.matches(person)) {
adults.add(person);
}
}
return adults;
}
doSomethingFor(adultPersons());
The above code can be simplified, using some library for collection filtering. Hamcrest provides an extension library named “hamcrest-collection”, which provides filtering functionality out of the box.
Integration with mockito
Hamcrest is integrated in various frameworks, such as Mockito. Mockito uses matchers to define method-call expectations. Which allows us to define expectations in a very expressive way.
No comments:
Post a Comment