All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.googlecode.catchexception.CatchException Maven / Gradle / Ivy

/**
 * Copyright (C) 2011 [email protected]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.googlecode.catchexception;

import com.googlecode.catchexception.apis.BDDCatchException;
import com.googlecode.catchexception.apis.CatchExceptionHamcrestMatchers;

/**
 * The methods of this class catch and verify exceptions in a single line of
 * code and make them available for further analysis.
 *
 * This Javadoc content is also available on the catch-exception web
 * page.
 *
 * 

Documentation

* * 1. How to use catch-exception? * 2. What is this stuff actually good for? * 3. How does it work internally? * 4. When is the caught exception reset? * 5. My code throws a ClassCastException. Why? * 6. The exception is not caught. Why? * 7. Do I have to care about memory leaks? * 8. The caught exception is not available in another thread. * Why? * 9. How do I catch an exception thrown by a static method? * 11. Can I catch errors instead of exceptions? * * * * * *

1. How to use catch-exception?

* * The most basic usage is: * import static com.googlecode.catchexception.CatchException.*; * * // call customerService.prepareBilling(Prize.Zero) * // and catch the exception if any is thrown * catchException(customerService).prepareBilling(Prize.Zero); * * // assert that an IllegalArgumentException was thrown * assert caughtException() instanceof IllegalArgumentException; * * You can combine the two lines of code in a single one if you like: * // call customerService.prepareBilling(Prize.Zero) * // and throw an ExceptionNotThrownAssertionError if * // the expected exception is not thrown * verifyException(customerService, IllegalArgumentException.class).prepareBilling(Prize.Zero); * There is a minor difference between both variants. In the first variant you * must start the JVM with option -ea to enable the assertion. The * second variant does not use JDK assertions and ,therefore, always verifies * the caught exception. * * A third variant allows you to select the type of exceptions you want to catch * (no verification involved): * // catch IllegalArgumentExceptions but no other exceptions * catchException(customerService, IllegalArgumentException.class).prepareBilling(Prize.Zero); * * The fourth and last variant verifies that some exception is thrown, i.e. the * type of the exception does not matter: * verifyException(customerService).prepareBilling(Prize.Zero); * * In all variants you can use caughtException() afterwards to * inspect the caught exception. * * Finally, there some alternative ways to catch and verify exceptions: *
    *
  • {@link BDDCatchException} - a BDD-like approach, *
  • {@link CatchExceptionHamcrestMatchers} - Hamcrest assertions *
*

2. What is this stuff actually good for?

* * This class targets concise and robust code in tests. Dadid Saff, a commiter * to JUnit, has discussed this approach in 2007. Let me summarize the arguments here. * * There are two advantages of the approach proposed here in comparison to the * use of try/catch blocks. *
    *
  • The test is more concise and easier to read. *
  • The test cannot be corrupted by a missing assertion. Assume you forgot to * type fail() behind the method call that is expected to throw an * exception. *
* * There are also some advantages of this approach in comparison to test * runner-specific mechanisms that catch and verify exceptions. *
    *
  • A single test can verify more than one thrown exception. *
  • The test can verify the properties of the thrown exception after the * exception is caught. *
  • The test can specify by which method call the exception must be thrown. *
  • The test does not depend on a specific test runner (JUnit4, TestNG). *
* *

3. How does it work internally?

* * The method catchException(obj) wraps the given object with a * proxy that catches the exception, then (optionally) verifies the exception, * and finally attaches the exception to the current thread for further analysis. The known limitations for proxies apply. * * Is both memory consumption and runtime a concern for you? Then use try/catch * blocks instead of this class. Because in this case the creation of proxies is * an unnecessary overhead. If only either memory consumption or runtime is an * issue for you, feel free to configure the cache of the underlying proxy * factories as appropriate. *

4. When is the caught exception reset?

* * The Method {@link #caughtException()} returns the exception thrown by the * last method call on a proxied object in the current thread, i.e. it is reset * by calling a method on the proxied object. If the called method has not * thrown an exception, caughtException() returns null. * * To reset the caught exception manually, call {@link #resetCaughtException()}. * At the moment there is no way to reset exceptions that have been caught in * other threads. *

5. My code throws a ClassCastException. Why?

* * Example: * StringBuilder sb = new StringBuilder(); * catchException(sb).charAt(-2); // throws ClassCastException * * Probably you have tested a final class. Proxy factories usually try to * subclass the type of the proxied object. This is not possible if the original * class is final. But there is a way out. If the tested method belongs to an * interface, then you can cast the argument (here: sb) to that * interface or ,easier, change the declared type of the argument to the * interface type. This works because the created proxy is not longer required * to have the same type as the original class but it must only have the same * interface. * // first variant * StringBuilder sb = new StringBuilder(); * catchException((CharSequence) sb).charAt(-2); // works fine * * // second variant * CharSequence sb = new StringBuilder(); * catchException(sb).charAt(-2); // works fine If the tested * method does no belong to an interface fall back to the try/catch-blocks or * use Powermock * . * // example for * PowerMock with JUnit4 * @RunWith(PowerMockRunner.class) * @PrepareForTest({ MyFinalType.class }) * public class MyTest { * *

6. Do I have to care about memory leaks?

* * This library uses a {@link ThreadLocal}. ThreadLocals are known to cause * memory leaks if they refer to a class the garbage collector would like to * collect. If you use this library only for testing, then memory leaks do not * worry you. If you use this library for other purposes than testing, you * should care. * *

7. The caught exception is not available in another thread. Why?

* * The caught exception is saved at the thread the * exception is thrown in. This is the reason the exception is not visible * within any other thread. *

8. Is there a way to get rid of the throws clause in my test * method?

* * Example: * public void testSomething() throws Exception { * ... * catchException(obj).do(); // do() throws a checked exception No, * although the exception is always caught you cannot omit the throws clause in * your test method. *

11. Can I catch errors instead of exceptions?

* * Yes, have a look at * {@code com.googlecode.catchexception.throwable.CatchThrowable} (in module * catch-throwable). * * @author rwoo * @since 16.09.2011 */ public class CatchException { /** * Returns the exception caught during the last call in the current thread. * * @param This type parameter makes some type casts redundant. * @return Returns the exception caught during the last call in the current * thread - if the call was made through a proxy that has been created via * {@link #verifyException(ThrowingCallable, Class) verifyException()} or * {@link #catchException(ThrowingCallable, Class) catchException()}. Returns * null when no exception was caught. */ public static E caughtException() { return ExceptionHolder.get(); } public static E caughtException(Class caughtExceptionType) { return ExceptionHolder.get(); } /** * Use it to verify that an exception is thrown and to get access to the * thrown exception (for further verifications). * * The following example verifies that obj.doX() throws a Exception: * verifyException(obj).doX(); // catch and verify * assert "foobar".equals(caughtException().getMessage()); // further analysis * * * If doX() does not throw a Exception, then a * {@link ExceptionNotThrownAssertionError} is thrown. Otherwise the thrown * exception can be retrieved via {@link #caughtException()}. * * * @param actor The instance that shall be proxied. Must not be * null. */ public static void verifyException(ThrowingCallable actor) { verifyException(actor, Exception.class); } /** * Use it to verify that an exception of specific type is thrown and to get * access to the thrown exception (for further verifications). * * The following example verifies that obj.doX() throws a MyException: * verifyException(obj, MyException.class).doX(); // catch and verify * assert "foobar".equals(caughtException().getMessage()); // further analysis * * * If doX() does not throw a MyException, then a * {@link ExceptionNotThrownAssertionError} is thrown. Otherwise the thrown * exception can be retrieved via {@link #caughtException()}. * * * @param actor The instance that shall be proxied. Must not be * null. * @param clazz The type of the exception that shall be thrown by the * underlying object. Must not be null. */ public static void verifyException(ThrowingCallable actor, Class clazz) { validateArguments(actor, clazz); catchException(actor, clazz, true); } /** * Use it to catch an exception and to get access to the thrown exception * (for further verifications). * * In the following example you catch exceptions that are thrown by * obj.doX(): * catchException(obj).doX(); // catch * if (caughtException() != null) { * assert "foobar".equals(caughtException().getMessage()); // further analysis * } * If doX() throws a exception, then {@link #caughtException()} * will return the caught exception. If doX() does not throw a * exception, then {@link #caughtException()} will return null. * * * @param actor The instance that shall be proxied. Must not be * null. */ public static void catchException(com.googlecode.catchexception.ThrowingCallable actor) { validateArguments(actor, Exception.class); catchException(actor, Exception.class, false); } /** * Use it to catch an exception of a specific type and to get access to the * thrown exception (for further verifications). * * In the following example you catch exceptions of type MyException that * are thrown by obj.doX(): * catchException(obj, MyException.class).doX(); // catch * if (caughtException() != null) { * assert "foobar".equals(caughtException().getMessage()); // further analysis * } * If doX() throws a MyException, then * {@link #caughtException()} will return the caught exception. If * doX() does not throw a MyException, then * {@link #caughtException()} will return null. If * doX() throws an exception of another type, i.e. not a * subclass but another class, then this exception is not thrown and * {@link #caughtException()} will return null. * * * @param actor The instance that shall be proxied. Must not be * null. * @param clazz The type of the exception that shall be caught. Must not be * null. */ public static void catchException(ThrowingCallable actor, Class clazz) { validateArguments(actor, clazz); catchException(actor, clazz, false); } private static void catchException(ThrowingCallable actor, Class clazz, boolean assertException) { resetCaughtException(); Exception exception = ExceptionCaptor.captureThrowable(actor); if (exception == null) { if (!assertException) { return; } else { throw new ExceptionNotThrownAssertionError(clazz); } } // is the thrown exception of the expected type? if (clazz.isAssignableFrom(exception.getClass())) { ExceptionHolder.set(exception); } else { if (assertException) { throw new ExceptionNotThrownAssertionError(clazz, exception); } else { ExceptionUtil.sneakyThrow(exception); } } } private static void validateArguments(ThrowingCallable actor, Class clazz) { if (actor == null) throw new IllegalArgumentException("obj must not be null"); if (clazz == null) throw new IllegalArgumentException("exceptionClazz must not be null"); } /** * Sets the {@link #caughtException() caught exception} to null. This does * not affect exceptions saved at threads other than the current one. * * Actually you probably never need to call this method because each method * call on a proxied object in the current thread resets the caught * exception. But if you want to improve test isolation or if you want to * 'clean up' after testing (to avoid memory leaks), call the method before * or after testing. */ public static void resetCaughtException() { ExceptionHolder.set(null); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy