I'm trying to write a unit test for a new service. The service returns void and uses other auto-wired services to do some work. I want to test a case where one of the internal services throws an exception which is caught by the top level service under test. How can I verify in the 'then' clause that the internal service threw an exception if it is caught by the service under test?
I would also like to verify that a static method was called to log the result, but I can't see a way to do that with Spock either. These are the only outputs for the service so I would like to verify that they are working.
To address the first part of your question:
Let's assume we have the following structure:
interface ServiceUnderTest {
void foo()
}
interface InternalService {
int bar() throws IllegalArgumentException
}
class ServiceUnderTestImpl implements ServiceUnderTest {
InternalService internalService
ServiceUnderTestImpl(InternalService internalService) {
this.internalService = internalService
}
void foo() {
try {
internalService.bar()
} catch(IllegalArgumentException ex) {
println "Got you"
}
}
}
You're testing method foo() in ServiceUnderTest and it returns void
So I suggest creating Stub of Internal Service that throws exception upon invocation of its bar method
The test will check that no exception has been thrown when calling foo (which means that the exception thrown by internal service was caught and handled correctly)
The test can look like this:
class SampleSpockTest extends Specification {
def "test sample" () {
given:
def internalService = Stub(InternalService)
def subject = new ServiceUnderTestImpl(internalService)
internalService.bar() >> {throw new IllegalArgumentException("Sample
exception")}
when:
subject.foo()
then:
noExceptionThrown()
}
}
I think that you shouldn't check in "then clause that the internal service threw an exception" as you say, actually you're checking the ServiceUnderTest. Its not optimal that it returns void, because you can't really know what did it do, maybe by checking its internal state after the invocation of method foo or something, but at least you can check that the exception was processed if internal service was called and threw and exception.
Regarding the static method mocking, you shouldn't do it probably, mocks do not play well with static calls. You can use "special" tools like PowerMock or PowerMockito, but I believe, they point on a code smell and should be primarily used for legacy code. Its only my opinion, though.
Related
I have a TestRunner class:
public class TestRunner {
private String result = "";
public void runTests(List<String> testClassNames) {
for (String testClassName : testClassNames) {
}
}
public String getResult() {
return result;
}
}
Then I have two test classes and I should run only the tests with #MyTest from both classes. getResult() should essentialy return "exampleTest() - OK" or "exampleTest() - FAILED", depending on if the test passes. Test class looks like this:
public class ExampleTests1 {
#MyTest (expected = IllegalStateException.class)
public void test1() {
throw new IllegalStateException();
}
#MyTest (expected = IllegalStateException.class)
public void test2() {
throw new RuntimeException();
}
}
I tried to do it with JUnitCore and Result, but couldn't find a way to get the test method name to add to the result. Is there another way?
You'll have to use reflection in this case.
This is kind of how JUnit works internally:
Since this sounds as a homework/assigment for educational purposes (If you're developing a "real" application - then just use JUnit or TestNG and don't write anything like this by yourself :) ), I won't provide a full solution however this is what you should do:
For each class identified by a className you should get a java.lang.Class that describes this class name. You can use Class.forName(...)
Then you should get all the methods of that class (by reflection) and for each method run the following:
2.1 Check whether the method is marked with an annotation #MyTest. If it doesn't - don't handle it
2.2 Also check whether the method name starts with test (String has startsWith method)
2.3 If you found out that the test class contains test methods, then:
2.3.1 Create an instance of the Test Class (probably you can assume that it has no-ops constructor, then use newInstance())
2.3.2 Run the method (again by reflection). Check the result / surround the execution call with try/catch block to intercept errors.
2.3.3 Print the Result as specified in the assignment :)
How can I assert that a certain exception is thrown inside an #Async method?
Because the following exception is caught by Springs SimpleAsyncUncaughtExceptionHandler.
#Service
public class Service {
#Async
public void run() {
throw new RuntimeException();
}
}
public class Test {
#Test
public void test() {
assertDoesNotThrow(() -> service.run()); //this always passes
}
}
If it is possible for your case, separate testing of asynchronicity and the actual unit of work. E.g. write test that will execute (no 'Async' functionality) Service.run() and assert that no/any/some exceptions are thrown.
In second test (utilizing #Async execution) you could test for the actual unit of work i.e. use your Spring provided bean and test for e.g.:
Awaitility.await().atMost(1000, TimeUnit.MILLISECONDS).untilAsserted(() -> runAnyCodeThatChecksTheResultOfYourServiceRunMethod());
Another method might be to replace the return type of the Service.run() method to java.util.concurrent.Future, Spring will then re-throw the exception. From AsyncExecutionAspectSupport.handleError javadoc:
"If the return type of the method is a {#link Future} object (again, if applicable), the original exception can be propagated by just throwing it at the higher level. However, for all other cases, the exception will not be transmitted back to the client."
import static org.awaitility.Awaitility.await;
Awaitility.await().atMost(5, TimeUnit.SECONDS)
.untilAsserted(() -> assertThrows(RuntimeException.class, () -> service.run()));
I'm making unit tests for a method that does some exception handling. here's the simplified class I'd like to test:
class Foo{
private BarService bar;
public int MethodToTest(){
try{
bar.methodThatThrows();
return 1;
}catch(Exception e){
return 0;
}
}
}
And this is the unit test class.
class FooTest{
private IBarService barService = mock(BarService.class);
#Test
TestMethodToTest(){
when(barService.methodThatThrows()).thenThrow(new Exception("message");
Foo foo = new foo();
ReflectionTestUtils.setField(foo, "barService", barService);
assertEquals(foo.MethodToTest(), 0);
}
}
Somehow, when I run it, it fails because there is an error thrown (as expected), which has the exact same message as the message I put into the mocked service. When I run in debug mode, the catch block is not even run. How can this be possible?
Most likely you are throwing a checked exception in your test that is not declared for methodThatThrows
A message you declared in your test is indeed printed to the console, but the message is more informative:
org.mockito.exceptions.base.MockitoException:
Checked exception is invalid for this method!
Invalid: java.lang.Exception: message
For example (IOException declared in BarService, but a more general checked exception thrown in the test code):
public class BarService {
public int methodThatThrows() throws IOException {
return 1;
}
}
You're not setting the BarService correctly in your example code. You're doing:
ReflectionTestUtils.setField(foo, "barService", barService);
But in the Foo class, the BarService variable is called "bar", and not "barService", so you need to do:
ReflectionTestUtils.setField(foo, "bar", barService);
Though the "correct" way to do this is to use Spring to autowire BarService into Foo, which allows you to avoid having to use ReflectionTestUtils in the first place.
My application has a flow that in the end of it the method System.exit(int) is being called.
I'm trying to test this flow by running a test using TestNG.
However, when running the test I'm getting this weird message although the test was completed:
Just for finding the root cause, I removed the System.exit(int) from the real flow and the test passed as expected, so the problem here is the System.exit(int) method.
In order to solve this issue I've tried to mock the problematic method but couldn't find the right way to do it. Here is what I did
I added java.lang.System.class under #PrepareForTest in the tests class.
added PowerMockito.mockStatic(java.lang.System.class) in the test
I've tried to mock the method in two ways:
a.
PowerMockito.replace(PowerMockito.method(System.class, "exit", int.class))
.with((proxy, method, args) -> null);
When running this way looks like the mock is not working, because I'm getting the same message at the end of the test which I also got when not applying any mocks on System.exit(int)
b.
PowerMockito.doNothing().when(System.class, "exit", Mockito.any());
In this way I'm getting this exception at the beginning of the test:
org.powermock.reflect.exceptions.MethodNotFoundException: No method found with name 'exit' with parameter types: [ <none> ] in class java.lang.System.
I already mocked some methods in this ways, not sure why with System.exit(int) it is not working.
Any ideas?
Thanks
Interesting question, I didn't know either, but apparently this is possible without the use of Powermock, through the use of SecurityManagers. Quoting from the original post:
Today I was writing test for one of our command line tools, and I had
this problem where the method dumping everything, which I really
needed to be called since that’s the outcome I was checking, also
called System.exit(). I had to find a way to test this anyway. I
thought about using PowerMock and mocking system but that would have
been complicated because I would have to find the exact class calling
the System.exit(). So here is another solution to avoid the
System.exit to exit (yes that’s possible I didn’t know about that
either).
The secrets lays in the SecurityManager mechanism of Java, this class
not only allows you to check permissions, but also to check exit
event. Therefore you can throw an exception if you want to stop the
exit
Below is a full sample I tested in IJ. Please note the sample is supposed to fail on purpose with:
java.lang.AssertionError:
Expected: is <10>
but: was <5>
Expected :is <10>
Actual :<5>
package com.example;
import org.junit.Test;
import java.security.Permission;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
public class SystemExitTest {
#Test
public void shouldExitWithSpecificCode() {
//save the initial security manager (probably null)
SecurityManager initialSecurityManger = System.getSecurityManager();
try {
// set security manager
System.setSecurityManager(new NoExitSecurityManager());
// execute code under test
new MyClass().exit(5);
// ensure this point is not reached by any chance
fail("Should've thrown ExitException");
} catch (ExitException e) {
// validate exit code
assertThat(e.status, is(10)); // <== this fails on purpose
} finally {
// restore initial security manager (otherwise a new ExitException will be thrown when the JVM will actually exit)
System.setSecurityManager(initialSecurityManger);
}
}
// class under test
public static class MyClass {
public void exit(int code) {
System.exit(code);
}
}
// exception to be thrown by security manager when System.exit is called
public static class ExitException extends SecurityException {
public final int status;
public ExitException(int status) {
this.status = status;
}
}
// custom security manager
public static class NoExitSecurityManager extends SecurityManager {
#Override
public void checkPermission(Permission perm) {
}
#Override
public void checkPermission(Permission perm, Object context) {
}
#Override
public void checkExit(int status) {
super.checkExit(status);
throw new ExitException(status);
}
}
}
Here is the code that I am working with. In this test I want to verify that the log method is being called when an exception is caught.
public class SuperClass(){
public void log()
{
do some logging;
}
}
public class ClassUnderTest extends SuperClass(){
public String methodbeingtested(Object param)
{
try
{
String a = SomeObject.
methodthatthrowsexception(param);//static method, throws JAXB/NPE
}
catch(Exception exp)
{
log("log msg",exp);//inherited method
}
}
}
public class ClassUnderTestTest {
#Test
public testmethodbeingtested(){
ClassUnderTest cut = new ClassUnderTest()
ClassUnderTest cutspy = Mockito.spy(cut);
cutspy.methodbeingtested(param);
Mockito.verify(cutspy).log("log msg", new Exception()); // exp is needed to here.
}
}
After looking at several samples, the above was the closest I could get. This testcase forces an exception. But it fails to verify the log method call as Mockito.verify requires the exact exception (exp) that is caught, which the test case does not have access to.
Is there any other way to test this scenario?
Mockito's verify method can be used with argument matchers. If you want to verify that log was called, with any Exception at all as the second argument, you can just write
verify(cutspy).log(eq("log msg"), any(Exception.class));
I've assumed that you have the right static imports for verify, eq and any.
As an aside, this test does not need PowerMock. Your line PowerMock.expectLastCall().once(); is both redundant and confusing, and should probably be removed, along with the #PrepareForTest annotation.
Instead of spying on ClassUnderTest, you should mock the logging framework, inject it into the class and then verify that the log method gets called. Id' also mock the SomeObject class and have it throw exception.
As an aside, you should really evaluate if you need to verify your log statements. Perhaps you have a valid reason to do so but typically, asserting/verifying to this extent is not required and will only make your tests brittle.