Spy a method parameter while testing with Mockito? - java

Suppose that I have a class like;
public class FooBar {
public int getMethod(List<String> code){
if(code.size() > 100)
throw new Exception;
return 0;
}
}
and I have a test class like this;
#RunWith(PowerMockRunner.class)
#PrepareForTest(FooBar.class)
public class FooBarTest{
FooBar fooBarInstance;
#Before
public void setUp() {
//MockitoAnnotations.initMocks(this);
fooBarInstance = new FooBar();
}
#Test(expected = Exception.class)
public void testGetCorrelationListCodesParameter() {
List<String> codes = Mockito.spy(new ArrayList<String>());
Mockito.doReturn(150).when(codes).size();
fooBarInstance.getMethod(codes);
}
}
How can I make this test method to throw an exception ? I've dealing for hours to do this. Well thanks anyway.

Spying is not needed, mocking is enough. As #David said, also mocking is not needed and not recommended for value object.
Using #Test(expected = Exception.class) has many drawbacks, test can pass when exception is thrown from not expected places. Test is not working but is visible as green.
I prefer BDD style testing with catch-exception.
Reasons for using catch-exceptions
(...) 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.
(...) 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).
import static com.googlecode.catchexception.CatchException.caughtException;
import static com.googlecode.catchexception.apis.CatchExceptionAssertJ.*;
public class FooBarTest {
FooBar sut = new FooBar(); // System Under Test
#Test
public void shouldThrowExceptionWhenListHasTooManyElements() {
when(sut).getMethod(listWithSize(150));
then(caughtException()).isInstanceOf(Exception.class);
}
private List<String> listWithSize(int size) {
return new ArrayList<String>(Arrays.asList(new String[size]));
}
}
Full working code for this test: https://gist.github.com/mariuszs/8543918
Not recommended solution with expected and mocking.
#RunWith(MockitoJUnitRunner.class)
public class FooBarTest {
#Mock
List<String> codes;
FooBar fooBarInstance = new FooBar();
#Test(expected = Exception.class)
public void shouldThrowExceptionWhenListHasTooManyElements() throws Exception {
when(codes.size()).thenReturn(150);
fooBarInstance.getMethod(codes);
}
}

A list is a value object. It's not something we should mock. You can write this whole test without mocking anything, if you're prepared to build a list that has a size in excess of 100.
Also, I prefer to use JUnit's ExpectedException mechanism, because it lets you check which line of the test method threw the exception. This is better than passing an argument to the #Test annotation, which only lets you check that the exception was thrown somewhere within the method.
public class FooBarTest {
#Rule
public ExpectedException exceptionRule = ExpectedException.none();
private FooBar toTest = new FooBar();
#Test
public void getMethodThrowsException_whenListHasTooManyElements() {
List<String> listWith101Elements =
new ArrayList<String>(Arrays.asList(new String[101]));
exceptionRule.expect(Exception.class);
toTest.getMethod(listWith101Elements);
}
}

Related

AEM JUnit java.lang.NullPointerException

I need to create a Junit test for a class in an AEM project and I'm having NullPointerException problems:
I create the ClassTestImpl
#ExtendWith({AemContextExtension.class, MockitoExtension.class})
class TestImpl {
private final AemContext ctx = new AemContext();
#Mock
private Test test;
#Mock
private ModelFactory modelFactory;
#BeforeEach
void setUp() throws Exception {
ctx.addModelsForClasses(TestImpl.class);
ctx.load().json("/com/project/core/models/adobe/TestImplTest.json","/content");
lenient().when(modelFactory.getModelFromWrappedRequest(eq(ctx.request()),
any(Resource.class), eq(Test.class)))
.thenReturn(test);
}
#Test
void testGetText() {
final String expected = "textTEST";
ctx.currentResource("/content/text");
Test test = ctx.request().adaptTo(Test.class);
String actual = test.getText();
assertEquals(expected,actual);
}
and the json structure:
"text": {
"jcr:primaryType": "nt:unstructured",
"sling:resourceType": "project/components/core/title",
"text": "textTEST"
}
}
when i Run test i give that result:
#Test
void testGetText() {
final String expected = "titleTEST";
ctx.currentResource("/content/title");
Title title = ctx.request().adaptTo(Title.class);
-->String actual = title[NullPointerException].getText();<--
assertEquals(expected,actual);
}
It looks like your model is a null reference. You do try to mock it with MockitoExtension but that's largely superfluous, given that you're also using AemContextExtension and it's probably the cause of the issue.
Null pointers aside, this code doesn't even test anything. Everything is mocked, even the Test class which I understand to be the subject under test.
Also, the parameter you're passing to addModelsForClasses looks like the test class (TestImpl) rather than the class of the Sling Model Test.
Instead of relying on Mockito, let the AEM Mocks library set up all the underlying objects by itself and make sure the class you're testing is the real thing, rather than a mock.
#ExtendWith(AemContextExtension.class)
class TestImpl {
private final AemContext ctx = new AemContext();
#BeforeEach
void setUp() throws Exception {
ctx.addModelsForClasses(Test.class); // Give it the Sling Model
ctx.load().json("/com/project/core/models/adobe/TestImplTest.json","/content");
}
#Test
void testGetText() {
final String expected = "textTEST";
ctx.currentResource("/content/text");
Test test = ctx.request().adaptTo(Test.class); // It'll use the actual class, not a mock this way
String actual = test.getText();
assertEquals(expected,actual);
}
}
See
https://sling.apache.org/documentation/development/sling-mock.html#sling-models-1
https://wcm.io/testing/aem-mock/usage-content-loader-builder.html

How to partial mock a method that throws exceptions using Mockito?

It's useful to test exception handling. In this specific case, I have a extractor that will do a specific task when an exception is thrown while unmarshaling a specific class.
Example Code
Below is a simplified example of the code. The production version is much more complicated.
public class Example {
public static enum EntryType {
TYPE_1,
TYPE_2
}
public static class Thing {
List<String> data = new ArrayList<String>();
EnumSet<EntryType> failedConversions = EnumSet.noneOf(EntryType.class);
}
public static class MyHelper {
public String unmarshal(String input) throws UnmarshalException {
// pretend this does more complicated stuff
return input + " foo ";
}
}
public static class MyService {
MyHelper adapter = new MyHelper();
public Thing process() {
Thing processed = new Thing();
try {
adapter.unmarshal("Type 1");
} catch (UnmarshalException e) {
processed.failedConversions.add(EntryType.TYPE_1);
}
// do some stuff
try {
adapter.unmarshal("Type 2");
} catch (UnmarshalException e) {
processed.failedConversions.add(EntryType.TYPE_2);
}
return processed;
}
}
}
Things I've Tried
Here's a list of things I've tried. For brevity, I haven't filled in all the mundane details.
Spying
The following method doesn't do anything and the exception doesn't throw. I'm not sure why.
#Test
public void shouldFlagFailedConversionUsingSpy()
throws Exception {
MyHelper spied = spy(fixture.adapter);
doThrow(new UnmarshalException("foo")).when(spied).unmarshal(
Mockito.eq("Type 1"));
Thing actual = fixture.process();
assertEquals(1, actual.failedConversions.size());
assertThat(actual.failedConversions.contains(EntryType.TYPE_1), is(true));
}
Mocking
The following didn't work because partial mocks don't seem to play well with methods that throw exceptions.
#Test
public void shouldFlagFailedConversionUsingMocks()
throws Exception {
MyHelper mockAdapter = mock(MyHelper.class);
when(mockAdapter.unmarshal(Mockito.anyString())).thenCallRealMethod();
when(mockAdapter.unmarshal(Mockito.eq("Type 2"))).thenThrow(
new UnmarshalException("foo"));
Thing actual = fixture.process();
assertEquals(1, actual.failedConversions.size());
assertThat(actual.failedConversions.contains(EntryType.TYPE_2), is(true));
}
ThenAnswer
This works, but I'm not sure if it's the proper way to do this:
#Test
public void shouldFlagFailedConversionUsingThenAnswer() throws Exception {
final MyHelper realAdapter = new MyHelper();
MyHelper mockAdapter = mock(MyHelper.class);
fixture.adapter = mockAdapter;
when(mockAdapter.unmarshal(Mockito.anyString())).then(
new Answer<String>() {
#Override
public String answer(InvocationOnMock invocation)
throws Throwable {
Object[] args = invocation.getArguments();
String input = (String) args[0];
if (input.equals("Type 1")) {
throw new UnmarshalException("foo");
}
return realAdapter.unmarshal(input);
}
});
Thing actual = fixture.process();
assertEquals(1, actual.failedConversions.size());
assertThat(actual.failedConversions.contains(EntryType.TYPE_1), is(true));
}
Question
Although the thenAnswer method works, it doesn't seem to be the proper solution. What is the correct way to perform a partial mock for this situation?
I'm not quite sure what you were getting at with the mocking and the spying, but you only really need to mock here.
First, I ran into a few snags when trying your mocks out for whatever reason. I believe this had to do with the spy call which was messed up in some way. I did eventually overcome these, but I wanted to get something simple to pass.
Next, I did notice something off with the way you were spying (the basis of my approach):
MyHelper spied = spy(fixture.adapter);
This implies that you want an instance of MyHelper mocked out, not spied. The worst part is that even if this object were fully hydrated, it wouldn't be properly injected since you haven't reassigned it to the test object (which I presume is fixture).
My preference is to use the MockitoJUnitRunner to help with the injection of mocked instances, and from there I build up a basis of what it is I actually need to mock.
There's only one mocked instance and then the test object, and this declaration will ensure that they're both instantiated and injected:
#RunWith(MockitoJUnitRunner.class)
public class ExampleTest {
#Mock
private MyHelper adapter;
#InjectMocks
private MyService fixture;
}
The idea is that you're injecting your mock into the fixture. You don't have to use this - you could use standard setters in a #Before declaration, but I prefer this since it greatly reduces the boilerplate code you have to write to get mocking to work.
Now there's only one change to be made: remove the spy instance and replace its previous usage with the actual mock.
doThrow(new UnmarshalException("foo")).when(adapter).unmarshal(eq("Type 1"));
With all of the code hoisted, this passes:
#RunWith(MockitoJUnitRunner.class)
public class ExampleTest {
#Mock
private MyHelper adapter;
#InjectMocks
private MyService fixture;
#Test
public void shouldFlagFailedConversionUsingSpy()
throws Exception {
doThrow(new UnmarshalException("foo")).when(adapter).unmarshal(eq("Type 1"));
Thing actual = fixture.process();
assertEquals(1, actual.failedConversions.size());
assertThat(actual.failedConversions.contains(Example.EntryType.TYPE_1), is(true));
}
}
Not being one to want to leave the question/use case incomplete, I circled around and replaced the test with the inner classes, and it works fine too:
#RunWith(MockitoJUnitRunner.class)
public class ExampleTest {
#Mock
private Example.MyHelper adapter;
#InjectMocks
private Example.MyService fixture;
#Test
public void shouldFlagFailedConversionUsingSpy()
throws Exception {
doThrow(new UnmarshalException("foo")).when(adapter).unmarshal(eq("Type 1"));
Example.Thing actual = fixture.process();
assertEquals(1, actual.failedConversions.size());
assertThat(actual.failedConversions.contains(Example.EntryType.TYPE_1), is(true));
}
}

Can junit test that a method will throw an exception?

Could you tell me please, is it normal practice to write a method (example: JUnit Test) that throws an Exception, for example:
class A {
public String f(int param) throws Exception {
if (param == 100500)
throw new Exception();
return "";
}
}
private A object = new A();
#Test
public void testSomething() throws Exception {
String expected = "";
assertEquals(object.f(5), expected);
}
In fact, method f() won't throw an exception for that parameter(5) but nevertheless I must declare that exception.
Yes it is completely fine, and if it does throw the exception the test will be considered as failed.
You need to specify that the method throws an Exception even if you know that the specific case does not (this check is done by the compiler).
In this case, what you expect is object.f(5) returns an empty string. Any other outcome (non-empty string or throwing an exception) would result in a failed test case.
A JUnit-Test is meant to test a given method for correct behavior. It is a perfectly valid scenario that the tested method throws an error (e.g. on wrong parameters). If it is a checked exception, you either have to add it to your test method declaration or catch it in the method and Assert to false (if the exception should not occur).
You can use the expected field in the #Test annotation, to tell JUnit that this test should pass if the exception occurs.
#Test(expected = Exception.class)
public void testSomething() throws Exception {
String expected = "";
assertEquals(object.f(5), expected);
}
In this case, the tested method should throw an exception, so the test will pass. If you remove the expected = Exception.class from the annotation, the test will fail if an exception occurs.
If the method you're calling throws a checked exception yes, you'll either need a try catch or to rethrow. It's fine to do this from the test itself. There are a variety of ways to test Exception using JUnit. I've tried to provide a brief summary below:
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
/**
* Example uses Kent Beck - Test Driven Development style test naming
* conventions
*/
public class StackOverflowExample {
#Rule
public ExpectedException expectedException = ExpectedException.none();
#Test
// Note the checked exception makes us re-throw or try / catch (we're
// re-throwing in this case)
public void calling_a_method_which_throws_a_checked_exception_which_wont_be_thrown() throws Exception {
throwCheckedException(false);
}
/*
* Put the class of the specific Exception you're looking to trigger in the
* annotation below. Note the test would fail if it weren't for the expected
* annotation.
*/
#Test(expected = Exception.class)
public void calling_a_method_which_throws_a_checked_exception_which_will_be_thrown_and_asserting_the_type()
throws Exception {
throwCheckedException(true);
}
/*
* Using ExpectedException we can also test for the message. This is my
* preferred method.
*/
#Test
public void calling_a_method_which_throws_a_checked_exception_which_will_be_thrown_and_asserting_the_type_and_message()
throws Exception {
expectedException.expect(Exception.class);
expectedException.expectMessage("Stack overflow example: checkedExceptionThrower");
throwCheckedException(true);
}
// Note we don't need to rethrow, or try / catch as the Exception is
// unchecked.
#Test
public void calling_a_method_which_throws_an_unchecked_exception() {
expectedException.expect(Exception.class);
expectedException.expectMessage("Stack overflow example: uncheckedExceptionThrower");
throwUncheckedException();
}
private void throwCheckedException(boolean willThrow) throws Exception {
// Exception is a checked Exception
if (willThrow) {
throw new Exception("Stack overflow example: checkedExceptionThrower");
}
}
private void throwUncheckedException() throws NullPointerException {
// NullPointerException is an unchecked Exception
throw new NullPointerException("Stack overflow example: uncheckedExceptionThrower");
}
}
You can test that the exception is launched with this:
#Test(expected = ValidationException.class)
public void testGreaterEqual() throws ValidationException {
Validate.greaterEqual(new Float(-5), 0f, "error7");
}

How to Write Integration test to check whether throwing exception in JAVA using TestNG?

I have a DAO which return some values and how to check a method throws an specific exception?
If you're using JUnit and you expect a test to throw a specific exception, do this:
#Test(expected = MyException.class)
public throwsExceptionWhenPassedAnIllegalValue() {
[...]
}
If you're using TestNG, similar syntax:
#Test(expectedExceptions = MyException.class)
public void throwsExceptionWhenPassedAnIllegalValue() {
[...]
}
If the exception that you're expecting is not thrown, these test methods will fail.

Expect exception when using JUnit Theory

In JUnit 4 you can declare expected exception using #Test(expected = SomeException.class) annotation. However, when testing is done using Theories, #Theory annotation does not have expected property.
What is the best way to declare expected exception when testing Theories?
I prefer using ExpectedException rule:
import org.junit.rules.ExpectedException;
<...>
#Rule
public ExpectedException thrown = ExpectedException.none();
#Theory
public void throwExceptionIfArgumentIsIllegal(Type type) throws Exception {
assumeThat(type, equalTo(ILLEGAL));
thrown.expect(IllegalArgumentException.class);
//perform actions
}
Also you can use a normal assert. You can use it on older versions of JUnit (before 4.9).
#Test
public void exceptionShouldIncludeAClearMessage() throws InvalidYearException {
try {
taxCalculator.calculateIncomeTax(50000, 2100);
fail("calculateIncomeTax() should have thrown an exception.");
} catch (InvalidYearException expected) {
assertEquals(expected.getMessage(),
"No tax calculations available yet for the year 2100");
}
}

Categories