Partial mock of template methods with EasyMock - java

I have a couple of classes following the "Template Method" pattern. Abstract class A, and concrete extensions, B and C. Like this:
public abstract class A
{
protected abstract String getData() throws SomeException;
public void doWork() throws OtherException
{
try
{
// business logic ...
String data = this.getData();
// more business logic ...
}
catch(SomeException e)
{
log("...", e);
throw new OtherException("...", e);
}
}
}
public Class B extends A
{
protected String getData() throws SomeException
{
// complicated logic relying on lots of dependencies
}
}
public Class C extends A
{
protected String getData() throws SomeException
{
// different but equally complicated logic relying on lots of dependencies
}
}
I want to write a test to verify when getData() throws SomeException that OtherException is thrown. I really want to avoid mocking up all of the complicated dependencies that would be required to force getData() to throw. I don't care how getData() throws, I just want it to throw. So I think a partial mock is what I want. This is what I have:
import static org.easymock.EasyMock.*;
....
#Test(expected = OtherException.class)
public void testSomethingOrAnother() throws Exception
{
B target = createMockBuilder(B.class).addMockedMethod("getData").createMock();
expect(target.getData()).andThrow(SomeException.class).once();
replay(target)
try
{
target.doWork(); // expect this to throw OtherException;
}
finally
{
verify(target);
}
}
The test looks good to me, but when I run it I get this:
java.lang.Exception: Unexpected exception, expected<OtherException> but was<java.lang.RuntimeException>
... deleted for brevity ...
Caused by: java.lang.RuntimeException: Ambiguous name: More than one method are named getData
at org.easymock.internal.ReflectionUtils.findMethod(ReflectionUtils.java:96)
at org.easymock.internal.ReflectionUtils.findMethod(ReflectionUtils.java:64)
at org.easymock.internal.MockBuilder.addMockedMethod(MockBuilder.java:73)
at org.easymock.internal.MockBuilder.addMockedMethods(MockBuilder.java:92)
at com.mycompany.more.packages.BTest(BTest.java:83)
... deleted for brevity ...
... 16 more
To be clear: There is NOT an overload of the getData() method anywhere in the hierarchy.
Is EasyMock able to do what I'm trying to do here? What am I missing?
relevant versions numbers:
EasyMock 3.0
JUnit 4.4
Java 1.6

I think your problem may be the use of the addMockedMethod(String). Not sure why EasyMock is complaining about an ambiguous method name if there are no overloads. But the following worked for me:
#Test
public void testSomethingOrAnother() {
B target = null;
try {
target = EasyMock.createMockBuilder(B.class).addMockedMethod(B.class.getDeclaredMethod("getData")).createMock();
EasyMock.expect(target.getData()).andThrow(new SomeException());
EasyMock.replay(target);
} catch (NoSuchMethodException e) {
fail(e.getMessage());
} catch (SomeException e) {
fail(e.getMessage());
}
try {
target.doWork();
fail("doWork should have thrown an exception");
} catch (OtherException e) {
//pass
}
}

With Easymock:3.2 you are able to specify types of the parameters of the method. Take a look IMockBuilder#addMockedMethod(String methodName,Class<?>... parameterTypes)
Thanks.

Related

Use a non-mandatory Exception to cause succeed on a junit test in java

I'm looking for a way to cause a succeed through an custom exception without expecting it all the time in junit4. Is this possible with a testrule or something, without touching every single testcase?
I know these options exist but then the exception is expected and the test fails, if no exception is thrown. I want the test to continue even if no exception is thrown and just use the exception to end the test in some special cases through aspectj.
#Test(TestSuccessException.class)
public void testCase() {
...
}
public class TestClass{
#Rule
public ExpectedException thrown = ExpectedException.none();
#Test
public void someTest() {
thrown.expect(MyRuntimeException.class);
...
}
}
As far as the junit4 source code looks, there isn't a way to achieve this.
The only way I found is by customizing the runner itself.
So something like this:
public class CustomTestRunner extends Runner {
private Class testClass;
public CustomTestRunner(Class testClass) {
super();
this.testClass = testClass;
}
#Override
public Description getDescription() {
return Description.EMPTY;
}
#Override
public void run(RunNotifier notifier) {
// Load all methods with #Test annotation from the given class and fire the execution
try {
Object testObject = testClass.getConstructor().newInstance();
for (Method method : testClass.getMethods()) {
if (method.isAnnotationPresent(Test.class)) {
fire(notifier, testObject, method);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void fire(RunNotifier notifier, Object testObject, Method method) throws IllegalAccessException, InvocationTargetException {
notifier.fireTestStarted(Description
.createTestDescription(testClass, method.getName()));
try {
// Call the test method
method.invoke(testObject);
} catch (InvocationTargetException e) {
// method.invoke wraps the original exception with InvocationTargetException
// The original exception is accessible via getCause()
// Check if the type of the original exception is the custom "early exist" exception
// If it doesn't match, throw the exception again; otherwise, ignore and mark the test as successful
if (!(e.getCause() instanceof EndTestEarlyException)) {
throw e;
}
}
notifier.fireTestFinished(Description
.createTestDescription(testClass, method.getName()));
}
}
You can use this by annotating the Test class as follows:
#RunWith(CustomTestRunner.class)
class MyIntegrationTest {
...
}
Note: Runner is the most generic Runner possible.
You could also attempt overriding a more specific runner if you already use one.
Edit:
As you are working with legacy, I intentionally tried not to use newer language features, like generics (Class<?>).
The solution is based on this baeldung article.
Junit5
Last but not least:
This is probably not relevant in your particular case but might be interesting for future readers.
If you manage to upgrade to Junit5, you could handle this within an extension.
You could implement a custom extension like this:
public class IgnoreEndTestEarlyException implements TestExecutionExceptionHandler {
#Override
public void handleTestExecutionException(ExtensionContext context,
Throwable throwable) throws Throwable {
if (throwable instanceof EndTestEarlyException ) {
return;
}
throw throwable;
}
}
And use it like this:
#ExtendWith(IgnoreEndTestEarlyException.class)
public class MyIntegrationTest
I tend to create another annotation (something like #IntegrationTest), put the #ExtendsWith on there, and use the new annotation.
It would be cleaner and easier to add multiple extensions.
You can run Junit4 and Junit5 within the same module, but you must replace all annotations within your integration test suit.
It might not be worth the effort for multiple thousand tests.
For Junit4 I found a better solution for my usecase. Just override the runChild Method from BlockJUnit4ClassRunner and add a try catch for the EndTestEarlyException.
#Override
protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
Description description = describeChild(method);
if (isIgnored(method)) {
notifier.fireTestIgnored(description);
} else {
Statement statement = new Statement() {
#Override
public void evaluate() throws Throwable {
try {
methodBlock(method).evaluate();
} catch (EndTestEarlyException e) {
System.out.println("EndTestEarlyException - ignore");
}
}
};
runLeaf(statement, description, notifier);
}
}

Java 8 - throw multiple generic checked exceptions in lambda

In a project I am working at, I have found a class which wraps all methods of its super-class in some elaborate exception handling. It looks similar to that:
public void method1() throws ExceptionA {
String exceptionString = "";
try {
super.method1();
} catch (ExceptionA e) {
exceptionString = // <convert the exception to string in an elaborate way>
throw e;
} finally {
// <an elaborate logger call which uses value of exceptionString>
}
}
public void method2() throws ExceptionB, ExceptionC {
String exceptionString = "";
try {
super.method2();
} catch (ExceptionB | ExceptionC e) {
exceptionString = // <convert the exception to string in elaborate way>
throw e;
} finally {
// <an elaborate logger call which uses value of exceptionString>
}
}
// ... <a bunch of other methods like this>
I immediately though "Wow, how could would it be to have one generic wrapper and just call it in every of these methods. The class would be like 10x shorter!".
So I got to work.
This is where I got stuck:
private interface ThrowingMethod<E extends Exception> {
void run() throws E;
}
public <E extends Exception> void wrapMethod(ThrowingMethod<E> method) throws E {
String exceptionString = "";
try {
method.run();
} catch (Exception e) {
exceptionString = // <convert the exception to string in an elaborate way>
throw e;
} finally {
// <an elaborate logger call which uses value of exceptionString>
}
}
public void method1() throws ExceptionA {
wrapMethod(super::method1); // works
}
public void method2() throws ExceptionB, ExceptionC {
wrapMethod(super::method2); // Error in Eclipse: "Unhandled exception type Exception"
}
// ... <a bunch of other methods like this>
In conclusion, this approach works for methods that throws only one type of checked exception. When method throws multiple checked exceptions, Java assumes that the exception type is Exception.
I tried to add more generic parameters to ThrowingMethod and wrapMethod but it doesn't change anything.
How can I get a functional interface to work with multiple generic exceptions?
When you expand your interface to use two type variables, i.e.
private static interface ThrowingMethod<E1 extends Exception,E2 extends Exception> {
void run() throws E1, E2;
}
public <E1 extends Exception,E2 extends Exception>
void wrapMethod(ThrowingMethod<E1,E2> method) throws E1,E2 {
// same as before
}
the rules regarding the type inference do not change and they are the same for both type variables. E.g. you can still use
public void method1() throws ExceptionA {
wrapMethod(super::method1);
}
as before, as the compiler simply infers the same single exception type for both type variables.
For the method declaring two exceptions, it won’t pick up one for the first type variable and the other for the second; there is no rule which could tell the compiler which exception to use for which type variable.
But you can help the compiler out in this case, e.g.
public void method2() throws ExceptionB, ExceptionC {
wrapMethod((ThrowingMethod<ExceptionB, ExceptionC>)super::method2);
}
which is the best you can get with this approach.
So your goal is just to wrap a bunch of methods with logging? A typical way to handle this is with AOP. You'd just create a single pointcut that matches all those methods, and you wouldn't have a bunch of repeated boilerplate. No need for those interfaces or wrapping methods.

Passing function as argument in JDK 8

I am trying to refactor the below code
class FileDownloadResource{
#Inject
private FileDownload fileDownload;
#Path(/abc)
public Response downloadFileABC(){
try{
fileDownload.downloadABC();
}catch(IOException e){
}
//Some code here that is common to the second method as well
}
#Path(/xyz)
public Response downloadFileXYZ(){
try{
fileDownload.downloadXYZ();
}catch(IOException e){
//handle exception
}
//Some code here that is common to the first method as well
}
}
The class is a JAX-RS rest resource. As you can see in the above code, everything except what is in the try block is the same for two method. Can we use any of the new JDK 8
features to pass fileDownload.downloadABC() as an argument to a private method ?
Basically, I am looking for some way to pass a function
as an argument and let the other part of the code be same.
Sure you can. You need either to use existing functional interface or create the new one. As you expect checked IOException, it's better to create the new one (you could also use Callable<Void>, but it's less convenient):
#FunctionalInterface
interface IORunnable {
void run() throws IOException;
}
Now you can create generic request handler private method:
public Response handleRequest(IORunnable r){
try {
r.run();
} catch(IOException e){
// handle exception
}
//Common code follows
}
And use it like this:
#Path("/abc")
public Response downloadFileABC(){
return handleRequest(fileDownload::downloadABC);
}
Or with lambda:
#Path("/abc")
public Response downloadFileABC(){
return handleRequest(() -> fileDownload.downloadABC());
}
You can do this, as long as the downloadABC() and downloadXYZ() methods have the same parameters and return value as the download() method of the Download interface.
Name of interface and interface method can be anything you choose.
#FunctionalInterface
interface DownloadMethod {
public void doDownload() throws IOException;
}
class FileDownload {
public void downloadABC() throws IOException {}
public void downloadXYZ() throws IOException {}
}
class FileDownloadResource{
#Inject
private FileDownload fileDownload;
#Path("/abc")
public Response downloadFileABC(){
return download(fileDownload::downloadABC);
}
#Path("/xyz")
public Response downloadFileXYZ() {
return download(fileDownload::downloadXYZ);
}
private Response download(DownloadMethod method){
try{
method.doDownload();
}catch(IOException e){
//handle exception
}
//Some code here that is common to both methods
}
}

Expect to throw exception without certain message

For example I've got a method and I want to test case when it throws certain exception type. There's no constructor without parameters in MyException class.
public class Clazz {
private Service service;
public void method() {
try {
int result = service.perform();
} catch (MyException exc) {
throw exc;
}
}
}
I created mock for service and set it into Clazz instance.
#Before
public void setUp() {
clazz = new Clazz();
service = createStrictMock(Service.class);
clazz.setService(service);
}
How should test look like? I mean how the test method for method should look like?
try:
expect(service.perform()).andThrow(new MyException(/* some params */));
For more details go to documentation (Working with Exceptions) or this thread
Because you rethrow exception you may also want to add #Test(expected=MyException.class) to the test method.
You could use the following (assuming your #setup works)
#Test(expected = MyException.class)
public void testExceptionThrown() throws Exception(){
Clazz subject = new Clazz();
subject.setService(service);
subject.method();
}

Throwing exceptions and catching them elsewhere

I was wondering if it was possible to write a method to throw an exception and have another method catch these exceptions.
For example,
public static void checkCircle() {
try {
checkPixel(a);
checkPixel(b);
checkPixel(c);
} catch (MyException e) {
System.out.println("not circle");
}
private static void checkPixel(anything) {
if (img.getRGB(xValue, yValue) != pOrigColour) {
throw new MyException();
}
}
class MyException extends Exception {
public MyException() {
}
public MyException(String msg) {
super(msg);
}
}
Thing is I want to the checkPixel method to throw a MyException, indicating that there is no circle, regardless of the results of the other calls.
Yes, it is possible. In your method declaration, you can add a throws clause, which indicates that your method might throw an exception.
In your case, you should modify your method declaration like this:
private static void checkPixel(anything) throws MyException {
// ...
}
You should note that exceptions should be used for... exceptional situations. Using them for simple error handling is highly unconventional, and adds unnecessary burden on users of your classes. In your case, you might want to add a boolean hasCircleAtLocation () method that would return true if there is a circle at the provided location.

Categories

Resources