Expect to throw exception without certain message - java

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();
}

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);
}
}

Javassist API with Junit

I am attempting to change some third party class definitions, before each test, to simulate different results. I have to use something like javassist because extending the classes, sometimes, is just not possible due to the access modifiers. Here is an example of what I am attempting to do with javassist and junit combined:
public class SimulatedSession extends SomeThirdParty {
private boolean isJoe = false;
public SimulatedSession(final boolean isJoe) {
this.isJoe = isJoe;
}
#Override
public void performThis() {
final ClassPool classPool = ClassPool.getDefault();
final CtClass internalClass = classPool.get("some.package.Class");
final CtMethod callMethod = internalClass.getDeclaredMethod("doThis");
if (isJoe) {
callMethod.setBody("{System.out.println(\"Joe\");}");
} else {
callMethod.setBody("{System.out.println(\"mik\");}");
}
internalClass.toClass();
}
}
#Test
public void firstTest() {
SimulatedSession toUse = new SimulatedSession(false);
// do something with this object and this flow
}
#Test
public void nextTest() {
SimulatedSession toUse = new SimulatedSession(true);
// do something with this object and this flow
}
if I run each test individually, I can run the code just fine. When I run them using the unit suite, one test after the other, I get a "frozen class issue". To get around this, I am looking at this post, however, I must admit I am unsure as to how one can use a different class pool to solve the issue.
Your current code will try to load twice the same class into the same ClassLoader which is forbidden, you can only load once a class for a given ClassLoader.
To make your unit tests pass, I had to:
Create my own temporary ClassLoader that will be able to load some.package.Class (that I replaced by javassist.MyClass for testing purpose) and that will be implemented in such way that it will first try to load the class from it before the parent's CL.
Set my own ClassLoader as context ClassLoader.
Change the code of SimulatedSession#performThis() to be able to get the class instance created by this method and to call internalClass.defrost() to prevent the "frozen class issue".
Invoke by reflection the method doThis() to make sure that I have different output by using the class instance returned by SimulatedSession#performThis() to make sure that the class used has been loaded with my ClassLoader.
So assuming that my class javassist.MyClass is:
package javassist;
public class MyClass {
public void doThis() {
}
}
The method SimulatedSession#performThis() with the modifications:
public Class<?> performThis() throws Exception {
final ClassPool classPool = ClassPool.getDefault();
final CtClass internalClass = classPool.get("javassist.MyClass");
// Prevent the "frozen class issue"
internalClass.defrost();
...
return internalClass.toClass();
}
The unit tests:
// The custom CL
private URLClassLoader cl;
// The previous context CL
private ClassLoader old;
#Before
public void init() throws Exception {
// Provide the URL corresponding to the folder that contains the class
// `javassist.MyClass`
this.cl = new URLClassLoader(new URL[]{new File("target/classes").toURI().toURL()}){
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
try {
// Try to find the class for this CL
return findClass(name);
} catch( ClassNotFoundException e ) {
// Could not find the class so load it from the parent
return super.loadClass(name, resolve);
}
}
};
// Get the current context CL and store it into old
this.old = Thread.currentThread().getContextClassLoader();
// Set the custom CL as new context CL
Thread.currentThread().setContextClassLoader(cl);
}
#After
public void restore() throws Exception {
// Restore the context CL
Thread.currentThread().setContextClassLoader(old);
// Close the custom CL
cl.close();
}
#Test
public void firstTest() throws Exception {
SimulatedSession toUse = new SimulatedSession(false);
Class<?> c = toUse.performThis();
// Invoke doThis() by reflection
Object o2 = c.newInstance();
c.getMethod("doThis").invoke(o2);
}
#Test
public void nextTest() throws Exception {
SimulatedSession toUse = new SimulatedSession(true);
Class<?> c = toUse.performThis();
// Invoke doThis() by reflection
Object o2 = c.newInstance();
c.getMethod("doThis").invoke(o2);
}
Output:
mik
Joe
Take a look at retransformer. It's a Javassist based lib I wrote for running tests just like this. It's a bit more terse than using raw Javassist.
Maybe another approach. We had a similar problem as we once mocked a dependency - we could not reset it. So we did the following: Before each test we replace the 'live' instance with our mock. After the tests, we restore the live instance. So I propose that you replace the modified instance of your third party code for each test.
#Before
public void setup()
{
this.liveBeanImpl = (LiveBean) ReflectionTools.getFieldValue(this.beanToTest, "liveBean");
ReflectionTools.setFieldValue(this.beanToTest, "liveBean", new TestStub());
}
#After
public void cleanup()
{
ReflectionTools.setFieldValue(this.beanToTest, "liveBean", his.liveBeanImpl);
}
The setFieldValue looks like this:
public static void setFieldValue(Object instanceToModify, String fieldName, Object valueToSet)
{
try
{
Field declaredFieldToSet = instanceToModify.getClass().getDeclaredField(fieldName);
declaredFieldToSet.setAccessible(true);
declaredFieldToSet.set(instanceToModify, valueToSet);
declaredFieldToSet.setAccessible(false);
}
catch (Exception exception)
{
String className = exception.getClass().getCanonicalName();
String message = exception.getMessage();
String errorFormat = "\n\t'%s' caught when setting value of field '%s': %s";
String error = String.format(errorFormat, className, fieldName, message);
Assert.fail(error);
}
}
So maybe your tests will pass if you reset your implementation for each test. Do you get the idea?

Partial mock of template methods with EasyMock

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.

EasyMock mocked method call is returning null when called for the second time after resetting mock object

This is my first post in Stackoverflow, so far I have been the active reader of this forum and I am posting my first question here.
This is regarding the EasyMock usage, I am a new user of EasyMock and in the following example code I am setting expectation for a collaborator method with the same object to be returned (doesn't matter whether it is same object or different object but the result is same) and I am resetting before going out of the test method. but when the second test is executed, the mocked method is returning null, I am not sure why is this happening.
If I run the methods
#RunWith(PowerMockRunner.class)
#PrepareForTest({CollaboratorWithMethod.class, ClassTobeTested.class})
public class TestClassTobeTested {
private TestId testId = new TestId();
#Test
public void testMethodtoBeTested() throws Exception{
CollaboratorWithMethod mockCollaborator = EasyMock.createMock(CollaboratorWithMethod.class);
PowerMock.expectNew(CollaboratorWithMethod.class).andReturn(mockCollaborator);
EasyMock.expect(mockCollaborator.testMethod("test")).andReturn(testId);
PowerMock.replay(CollaboratorWithMethod.class);
EasyMock.replay(mockCollaborator);
ClassTobeTested testObj = new ClassTobeTested();
try {
testObj.methodToBeTested();
} finally {
EasyMock.reset(mockCollaborator);
PowerMock.reset(CollaboratorWithMethod.class);
}
}
#Test
public void testMothedtoBeTestWithException() throws Exception {
CollaboratorWithMethod mockCollaborator = EasyMock.createMock(CollaboratorWithMethod.class);
PowerMock.expectNew(CollaboratorWithMethod.class).andReturn(mockCollaborator);
EasyMock.expect(mockCollaborator.testMethod("test")).andReturn(testId);
PowerMock.replay(CollaboratorWithMethod.class);
EasyMock.replay(mockCollaborator);
ClassTobeTested testObj = new ClassTobeTested();
try {
testObj.methodToBeTested();
} finally {
EasyMock.reset(mockCollaborator);
PowerMock.reset(CollaboratorWithMethod.class);
}
}
}
Here is my Collaborator class
public class CollaboratorWithMethod {
public TestId testMethod(String text) throws IllegalStateException {
if (text != null) {
return new TestId();
} else {
throw new IllegalStateException();
}
}
}
And here is my class under test
public class ClassTobeTested {
public static final CollaboratorWithMethod collaborator = new CollaboratorWithMethod();
public void methodToBeTested () throws IOException{
try {
TestId testid = collaborator.testMethod("test");
System.out.println("Testid returned "+ testid);
} catch (IllegalStateException e) {
throw new IOException();
}
}
}
I am looking for help from you guys to understand what exactly is happening here
The collaborator variable in class ClassTobeTested is final. The first test case initalizes the variable with mock object. The second test case cannot initialize the variable again. You set the expectation on the mock object created in second test case but actually the variable is still referring to the first mocked object.
Since you do not want to modify the class, you should set the mock reference once using #BeforeClass and make "mockCollaborator" a global variable so that you can use the reference in multiple test cases.

Delay EasyMock verification

I'm using EasyMock to create mock objects for JUnit testing in Java. I create a mock object and pass it to another thread where it expects methods to be called. In the other thread, the calls are enclosed in a try/catch(Throwable) block, so when an unexpected call occurs on the mock and it thus throws AssertionError, that error is caught by the catch block and treated. So, even though an unexpected call occurred, the test passes.
In order to have the test fail as expected, I would like to delay all verification of calls to the EasyMock.verify(mock) call made in the test-runner thread at the end. Is this possible and how?
I'm not sure about how to do this with EasyMock, but this behavior is possible with Mockito because verification assertions can be specified at the end of the test.
The correct solution I'd guess is to stop catching Throwable. Doing so catches all Errors as you're finding, which can be quite dangerous... are you absolutely positively 100% sure you need to catch Throwable? Why?
(If it turns out you do, you could catch AssertionError specifically and rethrow it. But that's ugly!)
Try using nice mocks:
http://easymock.org/EasyMock2_5_2_Documentation.html
"Nice Mocks
On a Mock Object returned by createMock() the default behavior for all methods is to throw an AssertionError for all unexpected method calls. If you would like a "nice" Mock Object that by default allows all method calls and returns appropriate empty values (0, null or false), use createNiceMock() instead. "
Default values will be returned for unexpected calls instead of throwing AssertionError, but you can still verify them with the verify() method (in which case the AssertionErrors will be thrown)
As #deterb suggested, it's possible with Mockito but you have to know the method name or you have to set expectations for every method. Here is an example:
The mocked interface:
public interface MyInterface {
void allowedMethod();
void disallowedMethod();
}
The user class which catches AssertionError:
public class UserClass {
public UserClass() {
}
public static void throwableCatcher(final MyInterface myInterface) {
try {
myInterface.allowedMethod();
myInterface.disallowedMethod();
} catch (final Throwable t) {
System.out.println("Catched throwable: " + t.getMessage());
}
}
}
And the Mockito test:
#Test
public void testMockito() throws Exception {
final MyInterface myInterface = mock(MyInterface.class);
UserClass.throwableCatcher(myInterface);
verify(myInterface, never()).disallowedMethod(); // fails here
}
The same is possible with EasyMock but it needs some work:
#Test
public void testEasyMock() throws Exception {
final AtomicBoolean called = new AtomicBoolean();
final MyInterface myInterface = createMock(MyInterface.class);
myInterface.allowedMethod();
myInterface.disallowedMethod();
final IAnswer<? extends Object> answer = new IAnswer<Object>() {
#Override
public Object answer() throws Throwable {
System.out.println("answer");
called.set(true);
throw new AssertionError("should not call");
}
};
expectLastCall().andAnswer(answer).anyTimes();
replay(myInterface);
UserClass.throwableCatcher(myInterface);
verify(myInterface);
assertFalse("called", called.get()); // fails here
}
Unfortunately you also have to know the method names here and you have to define expectations like myInterface.disallowedMethod() and expectLastCall().andAnswer(answer).anyTimes().
Another possibility is creating a proxy with the Proxy class (with a custom InvocationHandler) and using it as a mock object. It definitely needs more work but it could be the most customizable solution.
Finally don't forget that it's also possible to create a custom implementation with or without delegation to the EasyMock mock object. Here is one with delegation:
public class MockedMyInterface implements MyInterface {
private final MyInterface delegate;
private final AtomicBoolean called = new AtomicBoolean();
public MockedMyInterface(final MyInterface delegate) {
this.delegate = delegate;
}
#Override
public void allowedMethod() {
delegate.allowedMethod();
}
#Override
public void disallowedMethod() {
called.set(true);
throw new AssertionError("should not call");
}
public boolean isCalled() {
return called.get();
}
}
And the test for it:
#Test
public void testEasyMockWithCustomClass() throws Exception {
final MyInterface myInterface = createMock(MyInterface.class);
myInterface.allowedMethod();
final MockedMyInterface mockedMyInterface =
new MockedMyInterface(myInterface);
replay(myInterface);
UserClass.throwableCatcher(mockedMyInterface);
verify(myInterface);
assertFalse("called", mockedMyInterface.isCalled()); // fails here
}

Categories

Resources