Sometimes asserting custom exception is difficult. Sometimes it's not, for instance when you are using AssertJ, it's very quite easy to achieve. In this post I would like to highlight how to achieve that using Hamcrest matcher.
Below you might find a BaseExceptionMatcher which can be easily extended for our needs. Let's assume that we have an exception with four properties like errorType, errorCode, status and description.
import com.my.package.ErrorCode; import com.my.package.ErrorType; import com.my.package.base.BaseException; import com.my.package.MessageType; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; import javax.ws.rs.core.Response; public abstract class BaseExceptionMatcher extends TypeSafeMatcher<BaseException> { protected ErrorType expectedErrorType; protected ErrorCode expectedErrorCode; protected Response.Status expectedStatus; protected String expectedDescription; public BaseExceptionMatcher(ErrorCode expectedErrorCode, Response.Status expectedStatus, ErrorType expectedErrorType, String expectedDescription) { this.expectedErrorCode = expectedErrorCode; this.expectedStatus = expectedStatus; this.expectedErrorType = expectedErrorType; this.expectedDescription = expectedDescription; } @Override protected boolean matchesSafely(BaseException item) { return item.getStatus().equals(expectedStatus) && item.getError().stream().allMatch(p -> p.getType().equals(expectedErrorType.name()) && p.getCode().equals(expectedErrorCode.name())) && item.getError().stream().allMatch(p -> p.getDescription().contains(expectedDescription)); } @Override public void describeTo(Description description) { description.appendText("Response status : ") .appendValue(expectedStatus.getReasonPhrase() + " (" + expectedStatus.getStatusCode() + ")") .appendText(" | ErrorType : ").appendValue(expectedErrorType.name()) .appendText(" | ErrorCode : ").appendValue(expectedErrorCode.name()) .appendText(" | Description : ").appendValue(expectedDescription); } @Override protected void describeMismatchSafely(BaseException item, Description mismatchDescription) { mismatchDescription.appendText("Exception contains response status : ") .appendValue(item.getStatus().getReasonPhrase() + "(" + item.getStatus().getStatusCode() + ")") .appendText(" | ErrorType : ").appendValue(getMessageType(item).getType()) .appendText(" | ErrorCode : ").appendValue(getMessageType(item).getCode()) .appendText(" | Description : ").appendValue(getMessageType(item).getDescription()); } private MessageType getMessageType(BaseException item) { return item.getError().stream().findFirst().orElseGet(() -> { MessageType messageType = new MessageType(); messageType.setType(ErrorType.APPLICATION.name()); messageType.setCode(ErrorCode.INTERNAL_SERVER_ERROR.name()); messageType.setDescription("Matcher Error"); return messageType; }); } }
import my.package.ErrorCode; import my.package.ErrorType; import my.package.BaseExceptionMatcher; import javax.ws.rs.core.Response; public class MyExceptionMatcher extends BaseExceptionMatcher { public MyExceptionMatcher(ErrorType expectedErrorType, ErrorCode expectedErrorCode, Response.Status expectedStatus, String expectedDescription) { super(expectedErrorCode, expectedStatus, expectedErrorType, expectedDescription); } }
@Rule public ExpectedException expectedException = ExpectedException.none();
public void testFailure() throws MyException { expectedException.expect(new MyExceptionMatcher(ErrorType.APPLICATION, ErrorCode.DOCUMENT_NOT_FOUND, Response.Status.NOT_FOUND, "Document not found")); //do smth to fail }