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

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

/*
 * Copyright 2011-2024 the original author or authors.
 *
 * 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
 *
 *         https://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). */ public final class CatchException { /** * Prevents Instantiation of a new catch exception. */ private CatchException() { // Prevent instantiation } /** * 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(); } /** * Caught exception. * * @param * the element type * @param caughtExceptionType * the caught exception type * * @return the e * * @deprecated Use caughtXception() as passed argument is not used */ @Deprecated(since = "2.3.0", forRemoval = true) 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); } /** * Catch exception. * * @param actor * the actor * @param clazz * the clazz * @param assertException * the assert exception */ private static void catchException(ThrowingCallable actor, Class clazz, boolean assertException) { resetCaughtException(); var exception = ExceptionCaptor.captureThrowable(actor); if (exception == null) { if (!assertException) { return; } 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); } } /** * Validate arguments. * * @param actor * the actor * @param clazz * the clazz */ 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 - 2025 Weber Informatics LLC | Privacy Policy