Merge branch 'master' of ../controller
[yangtools.git] / common / testutils / src / main / java / org / opendaylight / yangtools / testutils / mockito / CallsRealOrExceptionAnswer.java
1 /*
2  * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.testutils.mockito;
9
10 import com.google.common.annotations.Beta;
11 import java.io.Serializable;
12 import java.lang.reflect.Modifier;
13 import org.mockito.Mockito;
14 import org.mockito.invocation.InvocationOnMock;
15 import org.mockito.stubbing.Answer;
16
17 /**
18  * Mockito Answer which for un-stubbed methods forwards the call to the real
19  * method if it is implemented on the mocked object (i.e. not an interface or
20  * abstract method), and otherwise throws an {@link UnstubbedMethodException}, like the
21  * {@link ThrowsMethodExceptionAnswer}.
22  *
23  * <p>
24  * This can be useful to create light-weight <a href=
25  * "http://googletesting.blogspot.ch/2013/07/testing-on-toilet-know-your-test-doubles.html">Fake Doubles</a>
26  * (in particular some with state). For example:
27  *
28  * <pre>
29  * import static ...testutils.mockito.MoreAnswers.realOrException;
30  *
31  * interface Service {
32  *     List&lt;Thing&gt; getThings();
33  *     boolean installThing(Thing thing);
34  * }
35  *
36  * abstract class FakeService implements Service {
37  *     // Ignore getThings() - we don't need that for this test
38  *     boolean installThing(Thing thing) {
39  *         LOGGER.log("not really installed");
40  *         return false;
41  *     }
42  * }
43  *
44  * Service fake = Mockito.mock(FakeService.class, realOrException())
45  * </pre>
46  *
47  * <p>
48  * TIP: An impact of Mockito is that, just like in standard Mockito, constructors
49  * (and thus field initializers) are not called. So in your abstract fake class,
50  * instead of:
51  *
52  * <pre>
53  * abstract class FakeService implements Service {
54  *     private final List&lt;Thing&gt; things = new ArrayList&lt;&gt;();
55  *
56  *     public List&lt;Thing&gt; getThings() {
57  *         return things;
58  *     }
59  *
60  *     &#64;Override
61  *     public boolean installThing(Thing thing) {
62  *         return things.add(thing);
63  *     }
64  * }
65  * </pre>
66  *
67  * <p>
68  * you'll just need to do:
69  *
70  * <pre>
71  * abstract class FakeService implements Service {
72  *     private List&lt;Thing&gt; things;
73  *
74  *     public List&lt;Thing&gt; getThings() {
75  *         if (things == null)
76  *             things = new ArrayList&lt;&gt;()
77  *         return things;
78  *     }
79  *
80  *     &#64;Override
81  *     public boolean installThing(Thing thing) {
82  *         return getThings().add(thing);
83  *     }
84  * }
85  * </pre>
86  *
87  * <p>
88  * The big advantage of Mikitos versus just writing classes implementing service
89  * interfaces without using Mockito at all is that you don't have to implement a
90  * lot of methods you don't care about - you can just make an abstract fake
91  * class (incl. e.g. an inner class in your Test) and implement only one or some
92  * methods. This keeps code shorter and thus more readable.
93  *
94  * <p>
95  * The advantage of Mikitos VS pure Mockito's when/thenAnswer are that they:
96  * <ul>
97  *
98  * <li>are fully type safe and refactoring resistant; whereas Mockito is not,
99  * e.g. for return values with doReturn(...).when(), and uses runtime instead of
100  * compile time error reporting for this.</li>
101  * <li>avoid confusion re. the alternative doReturn(...).when() syntax required
102  * with ThrowsMethodExceptionAnswer instead of when(...).thenReturn()</li>
103  * <li>enforce the ThrowsMethodExceptionAnswer by default for
104  * non-implemented methods (which is possible with Mockito by explicitly passing
105  * this, but is easily forgotten)</li>
106  * </ul>
107  *
108  * @see Mockito#mock(Class, Answer)
109  * @see ThrowsMethodExceptionAnswer
110  * @see Mockito#CALLS_REAL_METHODS
111  * @see Mockito#CALLS_REAL_METHODS
112  *
113  * @author Michael Vorburger
114  */
115 @Beta
116 public final class CallsRealOrExceptionAnswer implements Answer<Object>, Serializable {
117     private static final long serialVersionUID = -3730024662402964588L;
118     static final CallsRealOrExceptionAnswer INSTANCE = new CallsRealOrExceptionAnswer();
119
120     private CallsRealOrExceptionAnswer() {
121
122     }
123
124     @Override
125     public Object answer(final InvocationOnMock invocation) throws Throwable {
126         if (Modifier.isAbstract(invocation.getMethod().getModifiers())) {
127             throw new UnstubbedMethodException(invocation.getMethod(), invocation.getMock());
128         }
129         return invocation.callRealMethod();
130     }
131
132     Object readResolve() {
133         return INSTANCE;
134     }
135 }