Is there a way to handle unexpected exceptions in Spock? My use-case is to make test failures where exceptions are thrown better human-readable. E.g. when a test fails with an HttpClientErrorException, I would like to have the status code and the body in the printed test output.
e.g.
when:
restTemplate.getForObject(url, String)
then:
noExceptionThrown()
Now the getForObject() call throws an HttpClientErrorException I want an output like this:
Expected no exception to be thrown, but got 'HttpClientErrorException'
Status-Code: 400
Body: '{"error": "parameter foo missing"}'
You can write a custom extension to handle these exceptions yourself, you can't use noExceptionThrown() in this case, as this would prevent the exception to leave the feature method.
import spock.lang.*;
import java.lang.annotation.*;
import org.spockframework.runtime.extension.*;
import org.spockframework.runtime.model.*;
class NiceExceptionsInterceptor implements IMethodInterceptor {
static final NiceExceptionsInterceptor INSTANCE = new NiceExceptionsInterceptor()
void intercept(IMethodInvocation invocation) throws Throwable {
try {
invocation.proceed()
} catch (SpecialException e) {
throw new AssertionError("Special Exception happened: "+e.message)
}
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target([ElementType.TYPE, ElementType.METHOD])
#ExtensionAnnotation(NiceExceptionsExtensions)
#interface NiceExceptions {
}
class NiceExceptionsExtensions implements IAnnotationDrivenExtension<NiceExceptions> {
#Override
void visitSpecAnnotation(NiceExceptions annotation, SpecInfo spec) {
spec.bottomSpec.allFeatures.featureMethod*.addInterceptor(NiceExceptionsInterceptor.INSTANCE)
}
#Override
void visitFeatureAnnotation(NiceExceptions annotation, FeatureInfo feature) {
feature.featureMethod.addInterceptor(NiceExceptionsInterceptor.INSTANCE)
}
}
// ---- Usage DEMO
class SpecialException extends Exception {
SpecialException(String message) {
super(message)
}
}
#NiceExceptions
class ASpec extends Specification {
def "spec level"() {
when:
throw new SpecialException("Foo")
then:
true
}
}
class BSpec extends Specification {
#NiceExceptions
def "test level"() {
when:
throw new SpecialException("Foo")
then:
true
}
}
Try it in the Groovy Web Console
You can write you handler logic and extract any information in the catch-block.
catch (SpecialException e) {
throw new AssertionError("Special Exception happened: "+e.message)
}
Related
Hello I have wrote a test cases for my logic and all these are working nicely. however, I have no idea how to test my custom exceptions. My code below;
#Component
public class PlaneFactory {
public Plane getPlane(String planeType) {
if (StringUtils.isBlank(planeType)) {
throw new PlaneTypeNotFoundException();
}
if (planeType.equalsIgnoreCase("lightJet")) {
return new LightJet();
} else if (planeType.equalsIgnoreCase("midJet")) {
return new MidJet();
}
else {
throw new InvalidPlaneTypeException();
}
my custom exceptions below;
PlaneTypeNotFoundException class below;
public class PlaneTypeNotFoundException extends RuntimeException {
private static final long serialVersionUID = 4314211343358454345L;
public PlaneTypeNotFoundException() {
super("You have not enter anything to check a plane");
}
}
InvalidPlaneTypeException below;
public class InvalidPlaneTypeException extends RuntimeException {
public InvalidPlaneTypeException() {
super("You need to enter one of following plane types : {LightJet, MidJet}");
}
}
which methods are suitable to use ? I mean in this scenario should I use assertThrows or just use expected annotations ?
for PlaneTypeNotFoundException I have tried something below which it did not work
#Test
public void testPlaneFactory_isEmptyOrNull_ThenReturnException() {
String planeType = "";
LightJet lightJet= (LightJet) planeFactory.getPlane(planeType);
assertThrows(PlaneNotFoundException.class, () -> lightJet.getType().equalsIgnoreCase(planeType), "You have not enter anything to check a plane");
}
If I follow your code correctly then the executable lambda in assertThrows() should be the code that you expect to generate the exception:
public void testPlaneFactory_isEmptyOrNull_ThenReturnException() {
assertThrows(PlaneNotFoundException.class, () -> planeFactory.getPlane(""));
}
If it does throw an exception then the test should pass.
A test for the second case would be:
void testInvalidPlaneType() {
assertThrows(InvalidPlaneTypeException.class, () -> planeFactory.getPlane("doh"));
}
I want to custom error message in AOP around annotations.
I used to use #RestControllerAdvice before but It didn't work in AOP around method.
it outputs default error message.
I tried to input message in try ~ catch I know it's weird like //// 1 or //// 2
But I can't get to the point :(
TransactionAspect class
#Around("execution(* com.bono.server.controller..*(..))")
#Transactional
public Object caculatePerformanceTime(ProceedingJoinPoint proceedingJoinPoint) {
Object result = null;
try {
result = proceedingJoinPoint.proceed();
} catch (CustomeException e) { ////// 1
throw new ErrorMessage(CustomError.HTTP_400_MISTYPE);
}
catch (Throwable throwable) { /////// 2
return new ErrorMessage(CustomError.HTTP_400_MISTYPE);
}
return result;
}
ErrorMessage class
#Getter
#Setter
public class ErrorMessage {
private int errorCode;
private String errorMessage;
public ErrorMessage(CustomError customError) {
this.errorCode = customError.errorCode();
this.errorMessage = customError.errorMessage();
}
}
GroupExceptionAdvice class
#RestControllerAdvice
#Order(Ordered.HIGHEST_PRECEDENCE)
public class GroupExceptionAdvice extends ResponseEntityExceptionHandler {
//// 1
#ExceptionHandler(value = CustomeException.class)
public ResponseEntity<CustomErrorResponse> customhandleNotSatisfied(Exception ex, WebRequest request) {
CustomErrorResponse error = new CustomErrorResponse();
error.setTimestamp(LocalDateTime.now());
error.setError(ex.getMessage());
error.setStatus(HttpStatus.NOT_FOUND.value());
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
//// 2
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ExceptionHandler(value = CustomException.class)
public ErrorMessage handlerUnResolvedAddressException(MisTypingException e) {
return new ErrorMessage(CustomError.HTTP_400_MISTYPE);
}
}
{
"timestamp": "2019-08-12T01:14:16.467+0000",
"status": 500,
"error": "Internal Server Error",
"message": "class com.bono.server.config.exception.ErrorMessage cannot be cast to class org.springframework.http.ResponseEntity (com.bono.server.config.exception.ErrorMessage and org.springframework.http.ResponseEntity are in unnamed module of loader 'app')",
"path": "/bono/api/alarm"
}
I want to show like this
{
"code" : 102,
"message" : "got it !"
}
Converting my previous comments into an answer because the OP said that they solved his problem.
Your ErrorMessage class does not extend any Exception or Throwable, so how can you throw it? The code should not even compile and yield a compile error like:
No exception of type ErrorMessage can be thrown;
an exception type must be a subclass of Throwable
I.e. in your sample class I you ought to write something like
public class ErrorMessage extends Exception {
// (...)
}
for a checked exception or
public class ErrorMessage extends RuntimeException {
// (...)
}
for a non-checked exception. But your class definition does not extend anything, i.e. implicitly it directly extends Object.
I need to create a Rule to check for exceptions with customized messages. Below is my attempt, but this is not quite correct since I am simply using methods from the standard "ExpectedException". How to do it right?
public class CustomExpectedExceptionRule implements TestRule {
private final ExpectedException delegate = ExpectedException.none();
public static CustomExpectedExceptionRule none() {
return new CustomExpectedExceptionRule();
}
private CustomExpectedExceptionRule() {
}
public void expect(Class<? extends Throwable> type) {
delegate.expect(type);
}
public void expectMessage(String message) {
delegate.expectMessage(message);
}
#Override
public Statement apply(Statement base, Description description) {
return delegate.apply(base, description);
}
Now I'm trying something like that:
private final ExpectedException expectedException = ExpectedException.none();
private Object exception;
private String expectedMessage;
#Override
public Statement apply(Statement base, Description description) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
expectedException.expect((Class<? extends Throwable>) exception);
expectedException.expectMessage(expectedMessage);
base.evaluate();
}
};
}
public void expectedMessage(String expectedMessage) {
this.expectedMessage = expectedMessage;
}
public void expectedException(Object exception) {
this.exception = exception;
}
But this test does not work where the exception is thrown does not pass, though all fields here are passed.
How do I remake it in the correct form?
As I understand the requirement, in your tests you need to:
public class MyTest {
#Rule
ExpectedException expExc = ExpectedException.none();
#Test
public void throwsNothing() {
// "normal tests" not affected.
}
#Test
public void throwsExceptionWithSpecificTypeAndMessage() {
expExc.expect(MyCustomException.class);
expExc.expectMessage("substring, that passes test");// other matchers possible
// do something that (is expected to) raise(s)
// MyCustomException("substring, that passes test").
}
}
..where MyCustomException.class is a custom exception class (the lowest possible in inheritance hierarchy, which you want to "pass"), and substring, that passes test the (part of) the message, which you want to "pass".
Introducing a custom TestRule saves you 1 line/Test. In this simple case I would recommend you, not to implement the interface but extend ExternalResource (, see here)):
class CustomExpectedException extends ExternalResource /*implements (!) TestRule*/ {
private ExpectedException expExc = ExpectedException.none();
/* Parameterize the message and also the class, if it fits your needs,
* alternatively && additionally implement defaults/constants/more methods.*/
public void myExpect(String substr) {
expExc.expect(MyCustomException.class);
expExc.expectMessage(substr);// other matchers possible
}
}
...and then use it like:
public class MyTest {
#Rule
CustomExpectedException expExc = new CustomExpectedException();
...
#Test
public void throwsExceptionWithSpecificTypeAndMessage() {
expExc.myExpect("substring, that passes test");
// do something...
}
}
A rule-less approach(, see here) :
public class MyTest {
#Test
public void throwsExceptionWithSpecificTypeAndMessage() {
try { // !
// do something ...
// after that, fail the test:
org.junit.Assert.fail("expected exception!");
} catch (Exception exc) { // ! here i would recommend "the highest possible Exception" (in inheritance hierarchy) ...even better <code>Throwable</code>.
// this code can be moved to a (static) util method:
if (exc instanceof MyCustomException) {
// make assertions on ((MyCustomException) exc).getMessage();
} else {
org.junit.Assert.fail("UNexpected exception!");
// or rethrow:
// throw exc;
}
}
}
}
I would like to test the return code of an exception. Here is my production code:
class A {
try {
something...
}
catch (Exception e)
{
throw new MyExceptionClass(INTERNAL_ERROR_CODE, e);
}
}
And the corresponding exception:
class MyExceptionClass extends ... {
private errorCode;
public MyExceptionClass(int errorCode){
this.errorCode = errorCode;
}
public getErrorCode(){
return this.errorCode;
}
}
My unit test:
public class AUnitTests{
#Rule
public ExpectedException thrown= ExpectedException.none();
#Test (expected = MyExceptionClass.class,
public void whenRunningSomething_shouldThrowMyExceptionWithInternalErrorCode() throws Exception {
thrown.expect(MyExceptionClass.class);
??? expected return code INTERNAL_ERROR_CODE ???
something();
}
}
Simple:
#Test
public void whenSerialNumberIsEmpty_shouldThrowSerialNumberInvalid() throws Exception {
try{
whenRunningSomething_shouldThrowMyExceptionWithInternalErrorCode();
fail("should have thrown");
}
catch (MyExceptionClass e){
assertThat(e.getCode(), is(MyExceptionClass.INTERNAL_ERROR_CODE));
}
That is all you need here:
you don't want to expect that specific exception, as you want to check some properties of it
you know that you want to enter that specific catch block; thus you simply fail when the call doesn't throw
you don't need any other checking - when the method throws any other exception, JUnit will report that as error anyway
You can check for it using hamcres matchers as long as thrown.expect is overload to receive Matcher
thrown.expect(CombinableMatcher.both(
CoreMatchers.is(CoreMatchers.instanceOf(MyExceptionClass.class)))
.and(Matchers.hasProperty("errorCode", CoreMatchers.is(123))));
Note that you will need to add hamcrest matcher to your dependencies. Core matched that are included in JUnit is not enough.
Or if you don't want to use CombinableMatcher:
thrown.expect(CoreMatchers.instanceOf(MyExceptionClass.class));
thrown.expect(Matchers.hasProperty("errorCode", CoreMatchers.is(123));
Also, you don't need (expected = MyExceptionClass.class) declaration for #Test annotation
Expanding upon Sergii's answer, you can clean this up even more by writing a custom matcher.
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
public class CustomMatcher extends TypeSafeMatcher<CustomException> {
public static CustomMatcher hasCode(String item) {
return new CustomMatcher(item);
}
private String foundErrorCode;
private final String expectedErrorCode;
private CustomMatcher(String expectedErrorCode) {
this.expectedErrorCode = expectedErrorCode;
}
#Override
protected boolean matchesSafely(final CustomException exception) {
foundErrorCode = exception.getErrorCode();
return foundErrorCode.equalsIgnoreCase(expectedErrorCode);
}
#Override
public void describeTo(Description description) {
description.appendValue(foundErrorCode)
.appendText(" was not found instead of ")
.appendValue(expectedErrorCode);
}
}
The error code can then be checked like:
import org.junit.rules.ExpectedException;
public class MyObjTest {
#Rule
public ExpectedException thrown = ExpectedException.none();
#Test
public void someMethodThatThrowsCustomException() {
thrown.expect(CustomException.class);
thrown.expect(CustomMatcher.hasCode("110501"));
MyObj obj = new MyObj();
obj.methodThatThrowsCustomException();
}
}
Reference: https://dzone.com/articles/testing-custom-exceptions
Hey all I am trying to get these Unit tests to fail but can't it uses annotations, which is new to me. Any ideas would be great!
I have been trying all sorts of ways to get them to fail by either setting the test class variables to null, or trying to use if/else statements in the minimum test, but they always come out passing. Is this correct?
public class ValidationServiceTest extends BaseServiceTest {
ValidationService validationService;
ValidationException ve;
TestDto test;
Field f;
#Before
public void setup() {
validationService = new ValidationService();
ve = null;
}
#Test
public void validateNotNull(){
try {
validationService.validate(ve, test.xx);
assertNotNull("testing notNull()", ve);
} catch (Exception e) {
}
}
#Test
public void validateMin(){
try {
validationService.validate(ve, test.xy);
if(test.xy > f.min()){
assertTrue("testing min()" , test.xy > -1);
}
} catch (Exception e) {
}
}
public class TestDto{
#Field(notNull=true)
public Integer xx = null;
#Field(min=2)
public Integer xy = -5;
}
}
Do not catch Exception, mark test method with throws Exception instead.
The test field is never initialized in your test, which means it's null. This causes NullPointerException whenever you try to access fields of test. The assertion line is skipped and exception is suppressed in the catch clause. Removing the try/catch block and marking test method with throws Exception instead will cause the test to report error and you will see what's wrong instantly.