To check the number of interactions with a mock where the parameter in the method call is of a certain type, one can do
mock.someMethod(new FirstClass());
mock.someMethod(new OtherClass());
verify(mock, times(1)).someMethod(isA(FirstClass.class));
This will pass thanks to the call to isA since someMethod was called twice but only once with argument FirstClass
However, this pattern seems to not be possible when using an ArgumentCaptor, even if the Captor was created for the particular argument FirstClass
this doesn't work
mock.someMethod(new FirstClass());
mock.someMethod(new OtherClass());
ArgumentCaptor<FirstClass> captor = ArgumentCaptor.forClass(FirstClass.class);
verify(mock, times(1)).someMethod(captor.capture());
it says the mock was called more than once.
Is there any way to accomplish this verification while capturing the argument for further checking?
I recommend using Mockito's Hamcrest integration to write a good, clean matcher for it. That allows you to combine the verification with detailed checking of the passed argument:
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
verify(mock, times(1)).someMethod(argThat(personNamed("Bob")));
Matcher<Person> personNamed(final String name) {
return new TypeSafeMatcher<Person>() {
public boolean matchesSafely(Person item) {
return name.equals(item.getName());
}
public void describeTo(Description description) {
description.appendText("a Person named " + name);
}
};
}
Matchers generally lead to more readable tests and more useful test failure messages. They also tend to be very reusable, and you'll find yourself building up a library of them tailored for testing your project. Finally, you can also use them for normal test assertions using JUnit's Assert.assertThat(), so you get double use out of them.
Quoting the docs:
Note that an ArgumentCaptordon't do any type checks, it is only
there to avoid casting in your code. This might however change (type
checks could be added) in a future major release.
I wouldn't use an ArgumentCaptor for this. This class captures (literally) everything, despite what class was provided as it's .forClass argument.
To achieve what you want I suggest intercept the argument using Mockito's Answer interface:
private FirstClass lastArgument;
#Test
public void captureFirstClass() throws Exception {
doAnswer(captureLastArgument()).when(mock).someMethod(anInstanceOfFirstClass());
mock.someMethod(new FirstClass());
mock.someMethod(new OtherClass());
verify(mock, times(1)).someMethod(anInstanceOfFirstClass());
//write your desired matchers against lastArgument object
}
private Answer<FirstClass> captureLastArgument() {
return new Answer<FirstClass>() {
#Override
public FirstClass answer(InvocationOnMock invocation) throws Throwable {
TestClass.this.lastArgument = (FirstClass) invocation.getArguments()[0];
return null;
}
};
}
private static Object anInstanceOfFirstClass(){
return Mockito.argThat(isA(FirstClass.class));
}
You can use the the captor for the sake of capturing, then verify the number of invocations with each argument type separately.
// given
ArgumentCaptor<AA> captor = ArgumentCaptor.forClass(AA.class);
CC cc = new CC();
// when
cut.someMethod(new AA());
cut.someMethod(new BB());
cut.someMethod(new BB());
cut.someMethod(cc);
// then
Mockito.verify(collaborator, atLeastOnce()).someMethod(captor.capture());
Mockito.verify(collaborator, times(1)).someMethod(isA(AA.class));
Mockito.verify(collaborator, times(2)).someMethod(isA(BB.class));
Mockito.verify(collaborator, times(1)).someMethod(isA(CC.class));
assertEquals(cc, captor.getValue());
Apparently the generic type of the captor reference doesn't affect anything at runtime.
I also encountered this problem today. I thought I could simply do something like
verify(mock).someMethod(and(isA(FirstClass.class), captor.capture()));
but I couldn't get it to work. I ended up with this solution:
#Test
public void Test() throws Exception {
final ArgumentCaptor<FirstClass> captor = ArgumentCaptor.forClass(FirstClass.class);
mock.someMethod(new FirstClass());
mock.someMethod(new OtherClass());
verify(eventBus, atLeastOnce()).post(captor.capture());
final List<FirstClass> capturedValues = typeCheckedValues(captor.getAllValues(), FirstClass.class);
assertThat(capturedValues.size(), is(1));
final FirstClass capturedValue = capturedValues.get(0);
// Do assertions on capturedValue
}
private static <T> List<T> typeCheckedValues(List<T> values, Class<T> clazz) {
final List<T> typeCheckedValues = new ArrayList<>();
for (final T value : values) {
if (clazz.isInstance(value)) {
typeCheckedValues.add(value);
}
}
return typeCheckedValues;
}
Note: if only one class needs to be captured in this way typeCheckedValues can be simplified into:
private static List<FirstClass> typeCheckedValues(List<FirstClass> values) {
final List<FirstClass> typeCheckedValues = new ArrayList<>();
for (final Object value : values) {
if (value instanceof FirstClass) {
typeCheckedValues.add((FirstClass) value);
}
}
return typeCheckedValues;
}
Related
In short, I have a set of generated source code that I need to be able to dynamically mock based on external, non-Java configuration - they follow no consistent pattern / implement any interfaces other than being static, meaning I can only know how to mock a method at runtime and need to use PowerMockito to do so.
Say that I have this class:
public class SomeClass {
public static void doSomething(Integer i) {
throw new RuntimeException();
}
}
And I simply want to mock doSomething / have it not throw exceptions. To do that simply / without any of the complexity I mention in my use case, I could do this:
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
#PrepareForTest(SomeClass.class)
public class TestSomeClass {
#Test
public void testDoSomethingSimple() throws Exception {
PowerMockito.spy(SomeClass.class);
PowerMockito.doNothing().when(SomeClass.class, "doSomething", any(Integer.class));
SomeClass.doSomething(5);
}
}
Which works fine.
This changes however when we step back and try to address my needs, and move complexity to something like this:
#Test
public void testDoSomething() throws Exception {
// Below showing how everything could be externally-driven
testDoSomething("SomeClass", "doSomething", "java.lang.Integer");
SomeClass.doSomething(5);
}
public void testDoSomething(
final String canonicalClassName, final String methodName, final String... canonicalParameterClassNames)
throws Exception {
final Class<?> clazz = Class.forName(canonicalClassName);
PowerMockito.spy(clazz);
final Object[] argumentMatchers = new Object[canonicalParameterClassNames.length];
for (int i = 0; i < canonicalParameterClassNames.length; i++) {
argumentMatchers[i] = any(Class.forName(canonicalParameterClassNames[i]));
}
PowerMockito.doNothing().when(clazz, methodName, argumentMatchers);
}
Which leads to this issue:
After much head scratching, managed to replicate this error much more succinctly:
#Test
public void testDoSomethingIssueIsolated() throws Exception {
PowerMockito.spy(SomeClass.class);
Object matcher = any(Integer.class);
PowerMockito.doNothing().when(SomeClass.class, "doSomething", matcher);
SomeClass.doSomething(5);
}
Seemingly indicating that what's causing this issue is where the calls to create the argument matchers are, which is rather odd.
Got it - this isn't a PowerMockito thing. This is a standard Mockito thing and is actually by design - the telling point being one word in the error - you cannot use argument matchers outside of verification or stubbing. While I am using them for stubbing, the outside implies more.
This led me to this answer to another question on how matchers work, with the comment of particular importance:
- Call order isn't just important, it's what makes this all work.
Extracting matchers to variables generally doesn't work, because it
usually changes the call order. Extracting matchers to methods,
however, works great.
int between10And20 = and(gt(10), lt(20));
/* BAD */ when(foo.quux(anyInt(), between10And20)).thenReturn(true);
// Mockito sees the stack as the opposite: and(gt(10), lt(20)), anyInt().
public static int anyIntBetween10And20() { return and(gt(10), lt(20)); }
/* OK */ when(foo.quux(anyInt(), anyIntBetween10And20())).thenReturn(true);
// The helper method calls the matcher methods in the right order.
Basically have to be careful with the stack, which led me to this, which works and meets my requirements of being able to mock variable number of arguments determined at runtime (the Strings in testDoSomething could all have been pulled from a text file and the method call could be managed via reflection):
#Test
public void testDoSomething() throws Exception {
// Below showing how everything could be externally-driven
mockAnyMethod("SomeClass", "doSomething", "java.lang.Integer");
SomeClass.doSomething(5);
}
public void mockAnyMethod(
final String canonicalClassName,
final String methodName,
final String... canonicalParameterClassNames)
throws Exception {
final Class<?> clazz = Class.forName(canonicalClassName);
PowerMockito.spy(clazz);
PowerMockito.doNothing()
.when(clazz, methodName, getArgumentMatchers(canonicalParameterClassNames));
}
public Object[] getArgumentMatchers(final String... canonicalParameterClassNames)
throws ClassNotFoundException {
final Object[] argumentMatchers = new Object[canonicalParameterClassNames.length];
for (int i = 0; i < canonicalParameterClassNames.length; i++) {
argumentMatchers[i] = any(Class.forName(canonicalParameterClassNames[i]));
}
return argumentMatchers;
}
If you will read the failure trace carefully, you would have found your answer to this question
Misplaced or misused argument matcher detected here:
-> at mockito.TestSomeClass.testDoSomething(TestSomeClass.java:xx)
You cannot use argument matchers outside of verification or stubbing.
Examples of correct usage of argument matchers:
when(mock.get(anyInt())).thenReturn(null);
doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject());
verify(mock).someMethod(contains("foo"))
you try to use any(...) within the for loop, which is outside of verification or stubbing (here: PowerMockito.doNothing().when(...)).
for (int i = 0; i < canonicalParameterClassNames.length; i++) {
argumentMatchers[i] = any(Class.forName(canonicalParameterClassNames[i]));
}
PowerMockito.doNothing().when(clazz, methodName, argumentMatchers);
For this reason your solution would not work.
I tried this alternative
for (int i = 0; i < canonicalParameterClass.length; i++) {
PowerMockito.doNothing().when(clazz, methodName, any(Class.forName(canonicalParameterClass[i])));
}
which worked for me.
you could simplify your method by using Class instead of String for your class names.
#Test
public void testDoSomething() throws Exception {
// Below showing how everything could be externally-driven
testDoSomething(SomeClass.class, "doSomething", Integer.class);
SomeClass.doSomething(5);
}
public void testDoSomething(final Class classToTest, final String methodName, final Class... parameterClasses)
throws Exception {
PowerMockito.spy(classToTest);
for (int i = 0; i < parameterClasses.length; i++) {
PowerMockito.doNothing().when(classToTest, methodName, any(parameterClasses[i]));
}
}
}
Suppose I have a code to test
void myMethod()
{
byte []data = new byte[1];
data[0]='a';
output.send(42, data);
data[0]='b';
output.send(55, data);
}
I write a test:
testSubject.myMethod();
verify(output).send(eq(42), aryEq(new byte[]{'a'}));
verify(output).send(eq(55), aryEq(new byte[]{'b'}));
The test will fail as the method implementation reuses the same array for both calls, it is impossible to match args of the first send invocation after method finishes, so techically the verify statements should be specified before the method call, something like an expectation.
What is the right way to test such methods?
Well it looks like Mockito is a bit inconvenient here. It detects the method call and logs it (use mock(MyOutput.class, withSettings().verboseLogging()); to enable logging), but it stores the reference to the array you're passing and thus is influenced when you mutate the array. It then thinks the method call was send(42, [98]) rather than send(42, [97]).
A possible way to work with that is to use the expectations you've mentioned. For example you can use a counter and increment it if a call matched the expectations (it is really just a workaround and rather nasty):
MyOutput mock = mock(MyOutput.class, withSettings().verboseLogging());
Main subject = new Main(mock);
AtomicInteger correctCallsCounter = new AtomicInteger(0);
doAnswer(invocation -> correctCallsCounter.incrementAndGet()).when(mock).send(eq(42), aryEq(new byte[]{'a'}));
doAnswer(invocation -> correctCallsCounter.incrementAndGet()).when(mock).send(eq(55), aryEq(new byte[]{'b'}));
subject.myMethod();
assertThat(correctCallsCounter.get(), is(2));
It works, because doAnswer is triggered when the call happens and when the byte array hasn't been changed yet.
The big downside of this workaround is, that it only works with void methods. If send would return "something", then I currently don't see a way to work around that.
Well and the other one is that this is obviously a rather nasty workaround.
So I would suggest to refactor your code a bit (use a new array) if that is possible. That would avoid these issues here.
If your send method would indeed return something and your myMethod method would rely on it, then you would usually mock it this way (send is expected to return a String in this example):
when(mock.send(eq(55), aryEq(new byte[]{'b'}))).thenReturn("something");
In order to still use the above mentioned workaround, you could change the doAnswer method to increment your counter and return your String (which you would mock anyway, thus it isn't that bad):
doAnswer(invocation -> {
correctCallsCounter.incrementAndGet();
return "something";
}).when(mock).send(eq(42), aryEq(new byte[]{'a'}));
Use an Answer to copy the value of the parameter.
Here is some code (it is not pretty):
public class TestMyClass
{
private static List<byte[]> mockDataList = new ArrayList<>();
#InjectMocks
private MyClass classToTest;
private InOrder inOrder;
#Mock
private ObjectClass mockOutputClass;
#After
public void afterTest()
{
inOrder.verifyNoMoreInteractions();
verifyNoMoreInteractions(mockOutputClass);
}
#Before
public void beforeTest()
{
MockitoAnnotations.initMocks(this);
doAnswer(new Answer()
{
#Override
public Object answer(
final InvocationOnMock invocation)
throws Throwable
{
final byte[] copy;
final byte[] source = invocation.getArgument(1);
copy = new byte[source.length];
System.arraycopy(source, 0, copy, 0, source.length);
mockDataList.add(copy);
return null;
}
}).when(mockOutputClass).send(anyInt(), any(byte[].class));;
inOrder = inOrder(
mockOutputClass);
}
#Test
public void myMethod_success()
{
byte[] actualParameter;
final byte[] expectedFirstArray = { (byte)'a' };
final byte[] expectedSecondArray = { (byte)'b' };
classToTest.myMethod();
actualParameter = mockDataList.get(0);
assertArrayEquals(
expectedFirstArray,
actualParameter);
inOrder.verify(mockOutputClass).send(eq(42), any(byte[].class));
actualParameter = mockDataList.get(1);
assertArrayEquals(
expectedSecondArray,
actualParameter);
inOrder.verify(mockOutputClass).send(eq(55), any(byte[].class));
}
}
Note that the value of the parameter is compared separate of the verification of the call, but the order of the parameters is still verified (i.e. 'a' array first, then 'b' array).
When I used Powermock to mock a private method, and then verify it in another method's unit test, to check if it was called and with correct times. But I found I can verify whether the method was called and with the correct param, the called times of the method can be any number, however the unit test always passed. If someone can help me solve the problem. thx!
This is my source code:
public enum ConversionCommon{
INSTANCE;
...
public void getConvertTypeData(JSONObject result, JSONObject conversionData, String intent){
String uncertainty = "";
String unit1 = "";
String unit1_char = "";
if(result.has(ConversionDefine.CONVERSION_UNCERTAINTY)){
uncertainty = result.getString(ConversionDefine.CONVERSION_UNCERTAINTY);
}
if(result.has(ConversionDefine.CONVERSION_UNIT1)){
unit1 = result.getString(ConversionDefine.CONVERSION_UNIT1);
}
if(result.has(ConversionDefine.CONVERSION_UNIT1_CHAR)){
unit1_char = result.getString(ConversionDefine.CONVERSION_UNIT1_CHAR);
}
setUnitCount(conversionData, uncertainty, intent);
setDestUnits(conversionData, unit1, unit1_char);
}
}
The method getConvertTypeData() calls two methods once. The follow is my test case code:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ ConversionCommon.class, LengthConversion.class })
#PowerMockIgnore("javax.management.*")
public class ConversionCommonTest{
#Before
public void setUp() throws Exception{
PowerMockito.mockStatic(LengthConversion.class);//
PowerMockito.spy(LengthConversion.class); //
}
#Test
public void should_getConvertTypeData_invoke_setUnitCount() throws Exception{
JSONObject result = new JSONObject();
JSONObject conversionData = new JSONObject();
result.put(ConversionDefine.CONVERSION_UNCERTAINTY, "3");
result.put(ConversionDefine.CONVERSION_UNIT1, "meter");
result.put(ConversionDefine.CONVERSION_UNIT1_CHAR, "centmeter");
suppress(method(ConversionCommon.class, "setUnitCount", JSONObject.class, String.class, String.class));
ConversionCommon.INSTANCE.getConvertTypeData(result, conversionData, ConversionDefine.INTENT_LENGTH_CONVERSION);
PowerMockito.verifyPrivate(ConversionCommon.INSTANCE, Mockito.times(1)).invoke("setUnitCount", Mockito.anyObject(), Mockito.anyString(), Mockito.anyString());
}
The test case can run successful but if I change the last line to ...
PowerMockito.verifyPrivate(ConversionCommon.INSTANCE, Mockito.times(100)).invoke("setUnitCount", Mockito.anyObject(), Mockito.anyString(), Mockito.anyString());
... and re-run the test case. The result is also passed. But in fact I only call the method setUnitCount only once in Refo.
The edition of test framework: mockito:1.10.19;powermock:1.6.5
How can I correct verify the private method's call times with the API PowerMockito.verifyPrivate();
You are doing right, except one thing. You're spying on enum constant. And each enums constant is separate inner class. So you have to add two things:
Enum constant to #PrepareForTest with using fullyQualifiedNames, like `#PrepareForTest(values = { ConversionCommon.class, LengthConversion.class }, fullyQualifiedNames = "com.somepackage.ConversionCommon$INSTANCE")
Create a spy for instance: PowerMockito.spy(ConversionCommon.INSTANCE)
Replace constant with spy: Whitebox.setInternalState(ConversionCommon.class, spy)
Now, you can stub the spy method as usual doNothing().when(spy, "setUnitCount");
Code snippet for some abstract singleton implement via enum:
enum
public enum Singleton {
INSTANCE;
public void doSomething(){
callPrivateMethod();
}
private void callPrivateMethod() {
}
}
test
RunWith(PowerMockRunner.class)
#PrepareForTest(value = Singleton.class, fullyQualifiedNames = "com.stackoverflow.q46212600.Singleton$INSTANCE")
public class ClassUseSingletonTest {
#Test
public void should_verify_enum_method() throws Exception {
final Singleton spy = PowerMockito.spy(Singleton.INSTANCE);
Whitebox.setInternalState(Singleton.class, spy);
doNothing().when(spy, "callPrivateMethod");
new ClassUseSingletone().doSomething("do");
PowerMockito.verifyPrivate(Singleton.INSTANCE, times(1)).invoke("callPrivateMethod");
}
}
Full example, you may find here
I am a newbie to development and to unit tests in particular .
I guess my requirement is pretty simple, but I am keen to know others thoughts on this.
Suppose I have two classes like so -
public class First {
Second second ;
public First(){
second = new Second();
}
public String doSecond(){
return second.doSecond();
}
}
class Second {
public String doSecond(){
return "Do Something";
}
}
Let's say I am writing unit test to test First.doSecond() method. However, suppose, i want to Mock Second.doSecond() class like so. I am using Mockito to do this.
public void testFirst(){
Second sec = mock(Second.class);
when(sec.doSecond()).thenReturn("Stubbed Second");
First first = new First();
assertEquals("Stubbed Second", first.doSecond());
}
I am seeing that the mocking does not take effect and the assertion fails.
Is there no way to mock the member variables of a class that I want to test . ?
You need to provide a way of accessing the member variables so you can pass in a mock (the most common ways would be a setter method or a constructor which takes a parameter).
If your code doesn't provide a way of doing this, it's incorrectly factored for TDD (Test Driven Development).
This is not possible if you can't change your code. But I like dependency injection and Mockito supports it:
public class First {
#Resource
Second second;
public First() {
second = new Second();
}
public String doSecond() {
return second.doSecond();
}
}
Your test:
#RunWith(MockitoJUnitRunner.class)
public class YourTest {
#Mock
Second second;
#InjectMocks
First first = new First();
public void testFirst(){
when(second.doSecond()).thenReturn("Stubbed Second");
assertEquals("Stubbed Second", first.doSecond());
}
}
This is very nice and easy.
If you look closely at your code you'll see that the second property in your test is still an instance of Second, not a mock (you don't pass the mock to first in your code).
The simplest way would be to create a setter for second in First class and pass it the mock explicitly.
Like this:
public class First {
Second second ;
public First(){
second = new Second();
}
public String doSecond(){
return second.doSecond();
}
public void setSecond(Second second) {
this.second = second;
}
}
class Second {
public String doSecond(){
return "Do Something";
}
}
....
public void testFirst(){
Second sec = mock(Second.class);
when(sec.doSecond()).thenReturn("Stubbed Second");
First first = new First();
first.setSecond(sec)
assertEquals("Stubbed Second", first.doSecond());
}
Another would be to pass a Second instance as First's constructor parameter.
If you can't modify the code, I think the only option would be to use reflection:
public void testFirst(){
Second sec = mock(Second.class);
when(sec.doSecond()).thenReturn("Stubbed Second");
First first = new First();
Field privateField = PrivateObject.class.
getDeclaredField("second");
privateField.setAccessible(true);
privateField.set(first, sec);
assertEquals("Stubbed Second", first.doSecond());
}
But you probably can, as it's rare to do tests on code you don't control (although one can imagine a scenario where you have to test an external library cause it's author didn't :))
You can mock member variables of a Mockito Mock with ReflectionTestUtils
ReflectionTestUtils.setField(yourMock, "memberFieldName", value);
If you can't change the member variable, then the other way around this is to use powerMockit and call
Second second = mock(Second.class)
when(second.doSecond()).thenReturn("Stubbed Second");
whenNew(Second.class).withAnyArguments.thenReturn(second);
Now the problem is that ANY call to new Second will return the same mocked instance. But in your simple case this will work.
I had the same issue where a private value was not set because Mockito does not call super constructors. Here is how I augment mocking with reflection.
First, I created a TestUtils class that contains many helpful utils including these reflection methods. Reflection access is a bit wonky to implement each time. I created these methods to test code on projects that, for one reason or another, had no mocking package and I was not invited to include it.
public class TestUtils {
// get a static class value
public static Object reflectValue(Class<?> classToReflect, String fieldNameValueToFetch) {
try {
Field reflectField = reflectField(classToReflect, fieldNameValueToFetch);
reflectField.setAccessible(true);
Object reflectValue = reflectField.get(classToReflect);
return reflectValue;
} catch (Exception e) {
fail("Failed to reflect "+fieldNameValueToFetch);
}
return null;
}
// get an instance value
public static Object reflectValue(Object objToReflect, String fieldNameValueToFetch) {
try {
Field reflectField = reflectField(objToReflect.getClass(), fieldNameValueToFetch);
Object reflectValue = reflectField.get(objToReflect);
return reflectValue;
} catch (Exception e) {
fail("Failed to reflect "+fieldNameValueToFetch);
}
return null;
}
// find a field in the class tree
public static Field reflectField(Class<?> classToReflect, String fieldNameValueToFetch) {
try {
Field reflectField = null;
Class<?> classForReflect = classToReflect;
do {
try {
reflectField = classForReflect.getDeclaredField(fieldNameValueToFetch);
} catch (NoSuchFieldException e) {
classForReflect = classForReflect.getSuperclass();
}
} while (reflectField==null || classForReflect==null);
reflectField.setAccessible(true);
return reflectField;
} catch (Exception e) {
fail("Failed to reflect "+fieldNameValueToFetch +" from "+ classToReflect);
}
return null;
}
// set a value with no setter
public static void refectSetValue(Object objToReflect, String fieldNameToSet, Object valueToSet) {
try {
Field reflectField = reflectField(objToReflect.getClass(), fieldNameToSet);
reflectField.set(objToReflect, valueToSet);
} catch (Exception e) {
fail("Failed to reflectively set "+ fieldNameToSet +"="+ valueToSet);
}
}
}
Then I can test the class with a private variable like this. This is useful for mocking deep in class trees that you have no control as well.
#Test
public void testWithRectiveMock() throws Exception {
// mock the base class using Mockito
ClassToMock mock = Mockito.mock(ClassToMock.class);
TestUtils.refectSetValue(mock, "privateVariable", "newValue");
// and this does not prevent normal mocking
Mockito.when(mock.somthingElse()).thenReturn("anotherThing");
// ... then do your asserts
}
I modified my code from my actual project here, in page. There could be a compile issue or two. I think you get the general idea. Feel free to grab the code and use it if you find it useful.
If you want an alternative to ReflectionTestUtils from Spring in mockito, use
Whitebox.setInternalState(first, "second", sec);
Lots of others have already advised you to rethink your code to make it more testable - good advice and usually simpler than what I'm about to suggest.
If you can't change the code to make it more testable, PowerMock: https://code.google.com/p/powermock/
PowerMock extends Mockito (so you don't have to learn a new mock framework), providing additional functionality. This includes the ability to have a constructor return a mock. Powerful, but a little complicated - so use it judiciously.
You use a different Mock runner. And you need to prepare the class that is going to invoke the constructor. (Note that this is a common gotcha - prepare the class that calls the constructor, not the constructed class)
#RunWith(PowerMockRunner.class)
#PrepareForTest({First.class})
Then in your test set-up, you can use the whenNew method to have the constructor return a mock
whenNew(Second.class).withAnyArguments().thenReturn(mock(Second.class));
Yes, this can be done, as the following test shows (written with the JMockit mocking API, which I develop):
#Test
public void testFirst(#Mocked final Second sec) {
new NonStrictExpectations() {{ sec.doSecond(); result = "Stubbed Second"; }};
First first = new First();
assertEquals("Stubbed Second", first.doSecond());
}
With Mockito, however, such a test cannot be written. This is due to the way mocking is implemented in Mockito, where a subclass of the class to be mocked is created; only instances of this "mock" subclass can have mocked behavior, so you need to have the tested code use them instead of any other instance.
This is the second time I found myself writing this kind of code, and decided that there must be a more readable way to accomplish this:
My code tries to figure something out, that's not exactly well defined, or there are many ways to accomplish it. I want my code to try out several ways to figure it out, until it succeeds, or it runs out of strategies. But I haven't found a way to make this neat and readable.
My particular case: I need to find a particular type of method from an interface. It can be annotated for explicitness, but it can also be the only suitable method around (per its arguments).
So, my code currently reads like so:
Method candidateMethod = getMethodByAnnotation(clazz);
if (candidateMethod == null) {
candidateMethod = getMethodByBeingOnlyMethod(clazz);
}
if (candidateMethod == null) {
candidateMethod = getMethodByBeingOnlySuitableMethod(clazz);
}
if (candidateMethod == null) {
throw new NoSuitableMethodFoundException(clazz);
}
There must be a better way…
Edit: The methods return a method if found, null otherwise. I could switch that to try/catch logic, but that hardly makes it more readable.
Edit2: Unfortunately, I can accept only one answer :(
To me it is readable and understandable. I'd simply extract the ugly part of the code to a separate method (following some basic principles from "Robert C.Martin: Clean Code") and add some javadoc (and apologies, if necessary) like that:
//...
try {
Method method = MethodFinder.findMethodIn(clazz);
catch (NoSuitableMethodException oops) {
// handle exception
}
and later on in MethodFinder.java
/**
* Will find the most suitable method in the given class or throw an exception if
* no such method exists (...)
*/
public static Method findMethodIn(Class<?> clazz) throws NoSuitableMethodException {
// all your effort to get a method is hidden here,
// protected with unit tests and no need for anyone to read it
// in order to understand the 'main' part of the algorithm.
}
I think for a small set of methods what you're doing is fine.
For a larger set, I might be inclined to build a Chain of Responsibility, which captures the base concept of trying a sequence of things until one works.
I don't think that this is such a bad way of doing it. It is a bit verbose, but it clearly conveys what you are doing, and is easy to change.
Still, if you want to make it more concise, you can wrap the methods getMethod* into a class which implements an interface ("IMethodFinder") or similar:
public interface IMethodFinder{
public Method findMethod(...);
}
Then you can create instances of you class, put them into a collection and loop over it:
...
Method candidateMethod;
findLoop:
for (IMethodFinder mf: myMethodFinders){
candidateMethod = mf.findMethod(clazz);
if (candidateMethod!=null){
break findLoop;
}
}
if (candidateMethod!=null){
// method found
} else {
// not found :-(
}
While arguably somewhat more complicated, this will be easier to handle if you e.g. need to do more work between calling the findMethods* methods (such as more verification that the method is appropriate), or if the list of ways to find methods is configurable at runtime...
Still, your approach is probably OK as well.
I'm sorry to say, but the method you use seems to be the widely accepted one. I see a lot of code like that in the code base of large libraries like Spring, Maven etc.
However, an alternative would be to introduce a helper interface that can convert from a given input to a given output. Something like this:
public interface Converter<I, O> {
boolean canConvert(I input);
O convert(I input);
}
and a helper method
public static <I, O> O getDataFromConverters(
final I input,
final Converter<I, O>... converters
){
O result = null;
for(final Converter<I, O> converter : converters){
if(converter.canConvert(input)){
result = converter.convert(input);
break;
}
}
return result;
}
So then you could write reusable converters that implement your logic. Each of the converters would have to implement the canConvert(input) method to decide whether it's conversion routines will be used.
Actually: what your request reminds me of is the Try.these(a,b,c) method in Prototype (Javascript).
Usage example for your case:
Let's say you have some beans that have validation methods. There are several strategies to find these validation methods. First we'll check whether this annotation is present on the type:
// retention, target etc. stripped
public #interface ValidationMethod {
String value();
}
Then we'll check whether there's a method called "validate". To make things easier I assume, that all methods define a single parameter of type Object. You may choose a different pattern. Anyway, here's sample code:
// converter using the annotation
public static final class ValidationMethodAnnotationConverter implements
Converter<Class<?>, Method>{
#Override
public boolean canConvert(final Class<?> input){
return input.isAnnotationPresent(ValidationMethod.class);
}
#Override
public Method convert(final Class<?> input){
final String methodName =
input.getAnnotation(ValidationMethod.class).value();
try{
return input.getDeclaredMethod(methodName, Object.class);
} catch(final Exception e){
throw new IllegalStateException(e);
}
}
}
// converter using the method name convention
public static class MethodNameConventionConverter implements
Converter<Class<?>, Method>{
private static final String METHOD_NAME = "validate";
#Override
public boolean canConvert(final Class<?> input){
return findMethod(input) != null;
}
private Method findMethod(final Class<?> input){
try{
return input.getDeclaredMethod(METHOD_NAME, Object.class);
} catch(final SecurityException e){
throw new IllegalStateException(e);
} catch(final NoSuchMethodException e){
return null;
}
}
#Override
public Method convert(final Class<?> input){
return findMethod(input);
}
}
// find the validation method on a class using the two above converters
public static Method findValidationMethod(final Class<?> beanClass){
return getDataFromConverters(beanClass,
new ValidationMethodAnnotationConverter(),
new MethodNameConventionConverter()
);
}
// example bean class with validation method found by annotation
#ValidationMethod("doValidate")
public class BeanA{
public void doValidate(final Object input){
}
}
// example bean class with validation method found by convention
public class BeanB{
public void validate(final Object input){
}
}
You may use Decorator Design Pattern to accomplish different ways of finding out how to find something.
public interface FindMethod
{
public Method get(Class clazz);
}
public class FindMethodByAnnotation implements FindMethod
{
private final FindMethod findMethod;
public FindMethodByAnnotation(FindMethod findMethod)
{
this.findMethod = findMethod;
}
private Method findByAnnotation(Class clazz)
{
return getMethodByAnnotation(clazz);
}
public Method get(Class clazz)
{
Method r = null == findMethod ? null : findMethod.get(clazz);
return r == null ? findByAnnotation(clazz) : r;
}
}
public class FindMethodByOnlyMethod implements FindMethod
{
private final FindMethod findMethod;
public FindMethodByOnlyMethod(FindMethod findMethod)
{
this.findMethod = findMethod;
}
private Method findByOnlyMethod(Class clazz)
{
return getMethodOnlyMethod(clazz);
}
public Method get(Class clazz)
{
Method r = null == findMethod ? null : findMethod.get(clazz);
return r == null ? findByOnlyMethod(clazz) : r;
}
}
Usage is quite simple
FindMethod finder = new FindMethodByOnlyMethod(new FindMethodByAnnotation(null));
finder.get(clazz);
... I could switch that to try/catch logic, but that hardly makes it more readable.
Changing the signature of the get... methods so you can use try / catch would be a really bad idea. Exceptions are expensive and should only be used for "exceptional" conditions. And as you say, the code would be less readable.
What is bothering you is the repeating pattern used for flow control--and it should bother you--but there isn't too much to be done about it in Java.
I get really annoyed at repeated code & patterns like this, so for me it would probably be worth it to extract the repeated copy & paste control code and put it in it's own method:
public Method findMethod(Class clazz)
int i=0;
Method candidateMethod = null;
while(candidateMethod == null) {
switch(i++) {
case 0:
candidateMethod = getMethodByAnnotation(clazz);
break;
case 1:
candidateMethod = getMethodByBeingOnlyMethod(clazz);
break;
case 2:
candidateMethod = getMethodByBeingOnlySuitableMethod(clazz);
break;
default:
throw new NoSuitableMethodFoundException(clazz);
}
return clazz;
}
Which has the disadvantage of being unconventional and possibly more verbose, but the advantage of not having as much repeated code (less typos) and reads easier because of there being a little less clutter in the "Meat".
Besides, once the logic has been extracted into it's own class, verbose doesn't matter at all, it's clarity for reading/editing and for me this gives that (once you understand what the while loop is doing)
I do have this nasty desire to do this:
case 0: candidateMethod = getMethodByAnnotation(clazz); break;
case 1: candidateMethod = getMethodByBeingOnlyMethod(clazz); break;
case 2: candidateMethod = getMethodByBeingOnlySuitableMethod(clazz); break;
default: throw new NoSuitableMethodFoundException(clazz);
To highlight what's actually being done (in order), but in Java this is completely unacceptable--you'd actually find it common or preferred in some other languages.
PS. This would be downright elegant (damn I hate that word) in groovy:
actualMethod = getMethodByAnnotation(clazz) ?:
getMethodByBeingOnlyMethod(clazz) ?:
getMethodByBeingOnlySuitableMethod(clazz) ?:
throw new NoSuitableMethodFoundException(clazz) ;
The elvis operator rules. Note, the last line may not actually work, but it would be a trivial patch if it doesn't.