/**
* Copyright (C) 2011 rwoo@gmx.de
*
* 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 io.codearte.catchexception.shade.mockito.cglib.proxy.
MethodInterceptor;
import com.googlecode.catchexception.apis.
CatchExceptionBdd;
import com.googlecode.catchexception.apis.
CatchExceptionHamcrestMatchers;
import com.googlecode.catchexception.internal.
DelegatingInterceptor;
import com.googlecode.catchexception.internal.
ExceptionHolder;
import com.googlecode.catchexception.internal.
ExceptionProcessingInterceptor;
import com.googlecode.catchexception.internal.
InterfaceOnlyProxyFactory;
import com.googlecode.catchexception.internal.
SubclassProxyFactory;
/**
* The methods of this class catch and verify exceptions in <em>a single line of
* code</em> and make them available for further analysis.
* <p>
* This Javadoc content is also available on the <a
* href="http://code.google.com/p/catch-exception/" >catch-exception</a> web
* page.
*
* <h1>Documentation</h1>
* <p>
* <b> <a href="#1">1. How to use catch-exception?</a><br/>
* <a href="#2">2. What is this stuff actually good for?</a> <br/>
* <a href="#3">3. How does it work internally?</a><br/>
* <a href="#4">4. When is the caught exception reset?</a><br/>
* <a href="#5">5. My code throws a ClassCastException. Why?</a> <br/>
* <a href="#6">6. The exception is not caught. Why?</a> <br/>
* <a href="#7">7. Do I have to care about memory leaks?</a> <br/>
* <a href="#8">8. The caught exception is not available in another thread.
* Why?</a><br/>
* <a href="#9">9. How do I catch an exception thrown by a static method?</a> <br/>
* <a href="#11">11. Can I catch errors instead of exceptions?</a> <br/>
*
*
*
* </b>
* <p>
* <h3 id="1">1. How to use catch-exception?</h3>
* <p>
* The most basic usage is:
* <code><pre class="prettyprint lang-java">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;</pre></code>
* <p>
* You can combine the two lines of code in a single one if you like:
* <code><pre class="prettyprint lang-java">// call customerService.prepareBilling(Prize.Zero)
// and throw an ExceptionNotThrownAssertionError if
// the expected exception is not thrown
verifyException(customerService, IllegalArgumentException.class).prepareBilling(Prize.Zero);</pre></code>
* There is a minor difference between both variants. In the first variant you
* must start the JVM with option <code>-ea</code> to enable the assertion. The
* second variant does not use JDK assertions and ,therefore, always verifies
* the caught exception.
* <p>
* A third variant allows you to select the type of exceptions you want to catch
* (no verification involved):
* <code><pre class="prettyprint lang-java">// catch IllegalArgumentExceptions but no other exceptions
catchException(customerService, IllegalArgumentException.class).prepareBilling(Prize.Zero);</pre></code>
* <p>
* The fourth and last variant verifies that some exception is thrown, i.e. the
* type of the exception does not matter:
* <code><pre class="prettyprint lang-java">verifyException(customerService).prepareBilling(Prize.Zero);</pre></code>
* <p>
* In all variants you can use <code>caughtException()</code> afterwards to
* inspect the caught exception.
* <p>
* Finally, there some alternative ways to catch and verify exceptions:
* <ul>
* <li>{@link CatchExceptionBdd} - a BDD-like approach,
* <li> {@link CatchExceptionHamcrestMatchers} - Hamcrest assertions
* </ul>
* <h3 id="2">2. What is this stuff actually good for?</h3>
* <p>
* This class targets concise and robust code in tests. Dadid Saff, a commiter
* to JUnit, has <a
* href="http://shareandenjoy.saff.net/2006/12/assertthrownexception_20.html"
* >discussed</a> this approach in 2007. Let me summarize the arguments here.
* <p>
* There are two advantages of the approach proposed here in comparison to the
* use of try/catch blocks.
* <ul>
* <li>The test is more concise and easier to read.
* <li>The test cannot be corrupted by a missing assertion. Assume you forgot to
* type <code>fail()</code> behind the method call that is expected to throw an
* exception.
* </ul>
* <p>
* There are also some advantages of this approach in comparison to test
* runner-specific mechanisms that catch and verify exceptions.
* <ul>
* <li>A single test can verify more than one thrown exception.
* <li>The test can verify the properties of the thrown exception after the
* exception is caught.
* <li>The test can specify by which method call the exception must be thrown.
* <li>The test does not depend on a specific test runner (JUnit4, TestNG).
* </ul>
* <p>
* <h3 id="3">3. How does it work internally?</h3>
* <p>
* The method <code>catchException(obj)</code> wraps the given object with a
* proxy that catches the exception, then (optionally) verifies the exception,
* and finally attaches the exception to the current <a
* name="threadlocal">thread</a> for further analysis. The <a
* href="#proxies">known limitations</a> for proxies apply.
* <p>
* 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.
* <h3 id="4">4. When is the caught exception reset?</h3>
* <p>
* 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, <code>caughtException()</code> returns null.
* <p>
* 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.
* <h3 id="5"><a name="proxies" />5. My code throws a ClassCastException. Why?</h3>
* <p>
* Example:
* <code><pre class="prettyprint lang-java">StringBuilder sb = new StringBuilder();
catchException(sb).charAt(-2); // throws ClassCastException</pre></code>
* <p>
* 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: <code>sb</code>) 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.
* <code><pre class="prettyprint lang-java">// 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</pre></code> If the tested
* method does no belong to an interface fall back to the try/catch-blocks or
* use <a
* href="http://code.google.com/p/catch-exception/wiki/Dependencies">Powermock
* </a>.
* <code><pre class="prettyprint lang-java">// example for
PowerMock with JUnit4
@RunWith(PowerMockRunner.class)
@PrepareForTest({ MyFinalType.class })
public class MyTest {
</pre></code>
* <h3 id="6">6. The exception is not caught. Why?</h3>
* <p>
* Example:
* <code><pre class="prettyprint lang-java">ServiceImpl impl = new ServiceImpl();
catchException(impl).do(); // do() is a final method that throws an exception</pre></code>
* <p>
* Probably you have tested a final method. If that tested method belongs to an
* interface you could use {@link #interfaces(Object)} to fix that problem. But
* then the syntax starts to become ugly.
* <code><pre class="prettyprint lang-java">Service api = new ServiceImpl();
catchException(interfaces(api)).do(); // works fine</pre></code> I recommend
* to use try/catch blocks in such cases.
*
* <h3 id="7">7. Do I have to care about memory leaks?</h3>
* <p>
* 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.
* <p>
* <h3 id="8">8. The caught exception is not available in another thread. Why?</h3>
* <p>
* The caught exception is saved <a href="#threadlocal">at the thread</a> the
* exception is thrown in. This is the reason the exception is not visible
* within any other thread.
* <h3 id="9">9. How do I catch an exception thrown by a static method?</h3>
* <p>
* Unfortunately, catch-exception does not support this. Fall back on try/catch
* blocks.
* <h3 id="10">10. Is there a way to get rid of the throws clause in my test
* method?</h3>
* <p>
* Example:
* <code><pre class="prettyprint lang-java">public void testSomething() throws Exception {
...
catchException(obj).do(); // do() throws a checked exception</pre></code> No,
* although the exception is always caught you cannot omit the throws clause in
* your test method.
* <h3 id="11">11. Can I catch errors instead of exceptions?</h3>
* <p>
* Yes, have a look at
* {@link 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 on the proxied object
* in the current thread.
*
* @param <E>
* This type parameter makes some type casts redundant.
* @return Returns the exception caught during the last call on the proxied
* object in the current thread - if the call was made through a
* proxy that has been created via
* {@link #verifyException(Object, Class) verifyException()} or
* {@link #catchException(Object, Class) catchException()}. Returns
* null the proxy has not caught an exception. Returns null if the
* caught exception belongs to a class that is no longer
* {@link ClassLoader loaded}.
*/
public static <E extends
Exception> E
caughtException() {
return
ExceptionHolder.
get();
}
/**
* Use it to verify that an exception is thrown and to get access to the
* thrown exception (for further verifications).
* <p>
* The following example verifies that obj.doX() throws a Exception:
* <code><pre class="prettyprint lang-java">verifyException(obj).doX(); // catch and verify
assert "foobar".equals(caughtException().getMessage()); // further analysis
</pre></code>
* <p>
* If <code>doX()</code> does not throw a <code>Exception</code>, then a
* {@link ExceptionNotThrownAssertionError} is thrown. Otherwise the thrown
* exception can be retrieved via {@link #caughtException()}.
* <p>
*
* @param <T>
* The type of the given <code>obj</code>.
*
* @param obj
* The instance that shall be proxied. Must not be
* <code>null</code>.
* @return Returns an object that verifies that each invocation on the
* underlying object throws an exception.
*/
public static <T> T
verifyException(T
obj) {
return
verifyException(
obj,
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).
* <p>
* The following example verifies that obj.doX() throws a MyException:
* <code><pre class="prettyprint lang-java">verifyException(obj, MyException.class).doX(); // catch and verify
assert "foobar".equals(caughtException().getMessage()); // further analysis
</pre></code>
* <p>
* If <code>doX()</code> does not throw a <code>MyException</code>, then a
* {@link ExceptionNotThrownAssertionError} is thrown. Otherwise the thrown
* exception can be retrieved via {@link #caughtException()}.
* <p>
*
* @param <T>
* The type of the given <code>obj</code>.
*
* @param <E>
* The type of the exception that shall be caught.
* @param obj
* The instance that shall be proxied. Must not be
* <code>null</code>.
* @param clazz
* The type of the exception that shall be thrown by the
* underlying object. Must not be <code>null</code>.
* @return Returns an object that verifies that each invocation on the
* underlying object throws an exception of the given type.
*/
public static <T, E extends
Exception> T
verifyException(T
obj,
Class<E>
clazz) {
return
processException(
obj,
clazz, true);
}
/**
* Use it to catch an exception and to get access to the thrown exception
* (for further verifications).
* <p>
* In the following example you catch exceptions that are thrown by
* obj.doX():
* <code><pre class="prettyprint lang-java">catchException(obj).doX(); // catch
if (caughtException() != null) {
assert "foobar".equals(caughtException().getMessage()); // further analysis
}</pre></code>
* If <code>doX()</code> throws a exception, then {@link #caughtException()}
* will return the caught exception. If <code>doX()</code> does not throw a
* exception, then {@link #caughtException()} will return <code>null</code>.
* <p>
*
* @param <T>
* The type of the given <code>obj</code>.
*
* @param obj
* The instance that shall be proxied. Must not be
* <code>null</code>.
* @return Returns a proxy for the given object. The proxy catches
* exceptions of the given type when a method on the proxy is
* called.
*/
public static <T> T
catchException(T
obj) {
return
processException(
obj,
Exception.class, false);
}
/**
* Use it to catch an exception of a specific type and to get access to the
* thrown exception (for further verifications).
* <p>
* In the following example you catch exceptions of type MyException that
* are thrown by obj.doX():
* <code><pre class="prettyprint lang-java">catchException(obj, MyException.class).doX(); // catch
if (caughtException() != null) {
assert "foobar".equals(caughtException().getMessage()); // further analysis
}</pre></code>
* If <code>doX()</code> throws a <code>MyException</code>, then
* {@link #caughtException()} will return the caught exception. If
* <code>doX()</code> does not throw a <code>MyException</code>, then
* {@link #caughtException()} will return <code>null</code>. If
* <code>doX()</code> 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 <code>null</code>.
* <p>
*
* @param <T>
* The type of the given <code>obj</code>.
*
* @param <E>
* The type of the exception that shall be caught.
* @param obj
* The instance that shall be proxied. Must not be
* <code>null</code>.
* @param clazz
* The type of the exception that shall be caught. Must not be
* <code>null</code>.
* @return Returns a proxy for the given object. The proxy catches
* exceptions of the given type when a method on the proxy is
* called.
*/
public static <T, E extends
Exception> T
catchException(T
obj,
Class<E>
clazz) {
return
processException(
obj,
clazz, false);
}
/**
* Creates a proxy that processes exceptions thrown by the underlying
* object.
* <p>
* Delegates to
* {@link SubclassProxyFactory#createProxy(Class, MethodInterceptor)} which
* itself might delegate to
* {@link InterfaceOnlyProxyFactory#createProxy(Class, MethodInterceptor)}.
*/
@
SuppressWarnings("javadoc")
private static <T, E extends
Exception> T
processException(T
obj,
Class<E>
exceptionClazz, boolean
assertException) {
if (
obj == null) {
throw new
IllegalArgumentException("obj must not be null");
}
return new
SubclassProxyFactory().<T>
createProxy(
obj.
getClass(),
new
ExceptionProcessingInterceptor<E>(
obj,
exceptionClazz,
assertException));
}
/**
* Returns a proxy that implements all interfaces of the underlying object.
*
* @param <T>
* must be an interface the object implements
* @param obj
* the object that created proxy will delegate all calls to
* @return Returns a proxy that implements all interfaces of the underlying
* object and delegates all calls to that underlying object.
*/
public static <T> T
interfaces(T
obj) {
if (
obj == null) {
throw new
IllegalArgumentException("obj must not be null");
}
return new
InterfaceOnlyProxyFactory().<T>
createProxy(
obj.
getClass(),
new
DelegatingInterceptor(
obj));
}
/**
* Sets the {@link #caughtException() caught exception} to null. This does
* not affect exceptions saved at threads other than the current one.
* <p>
* 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);
}
}