All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opendaylight.infrautils.testutils.mockito.CallsRealOrExceptionAnswer Maven / Gradle / Ivy

There is a newer version: 7.0.3
Show newest version
/*
 * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.infrautils.testutils.mockito;

import java.io.Serializable;
import java.lang.reflect.Modifier;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

/**
 * Mockito Answer which for un-stubbed methods forwards the call to the real
 * method if it is implemented on the mocked object (i.e. not an interface or
 * abstract method), and otherwise throws an {@link UnstubbedMethodException}, like the
 * {@link ThrowsMethodExceptionAnswer}.
 *
 * 

* This can be useful to create light-weight Fake Doubles * (in particular some with state). For example: * *

 * import static ...testutils.mockito.MoreAnswers.realOrException;
 *
 * interface Service {
 *     List<Thing> getThings();
 *     boolean installThing(Thing thing);
 * }
 *
 * abstract class FakeService implements Service {
 *     // Ignore getThings() - we don't need that for this test
 *     boolean installThing(Thing thing) {
 *         LOGGER.log("not really installed");
 *         return false;
 *     }
 * }
 *
 * Service fake = Mockito.mock(FakeService.class, realOrException())
 * 
* *

* TIP: An impact of Mockito is that, just like in standard Mockito, constructors * (and thus field initializers) are not called. So in your abstract fake class, * instead of: * *

 * abstract class FakeService implements Service {
 *     private final List<Thing> things = new ArrayList<>();
 *
 *     public List<Thing> getThings() {
 *         return things;
 *     }
 *
 *     @Override
 *     public boolean installThing(Thing thing) {
 *         return things.add(thing);
 *     }
 * }
 * 
* *

* you'll just need to do: * *

 * abstract class FakeService implements Service {
 *     private List<Thing> things;
 *
 *     public List<Thing> getThings() {
 *         if (things == null)
 *             things = new ArrayList<>()
 *         return things;
 *     }
 *
 *     @Override
 *     public boolean installThing(Thing thing) {
 *         return getThings().add(thing);
 *     }
 * }
 * 
* *

* The big advantage of this versus just writing classes implementing service * interfaces without using Mockito at all is that you don't have to implement a * lot of methods you don't care about - you can just make an abstract fake * class (incl. e.g. an inner class in your Test) and implement only one or some * methods. This keeps code shorter and thus more readable. * *

* The advantage of this VS pure Mockito's when/thenAnswer are that they: *

    * *
  • are fully type safe and refactoring resistant; whereas Mockito is not, * e.g. for return values with doReturn(...).when(), and uses runtime instead of * compile time error reporting for this.
  • *
  • avoid confusion re. the alternative doReturn(...).when() syntax required * with ThrowsMethodExceptionAnswer instead of when(...).thenReturn()
  • *
  • enforce the ThrowsMethodExceptionAnswer by default for * non-implemented methods (which is possible with Mockito by explicitly passing * this, but is easily forgotten)
  • *
* * @see Mockito#mock(Class, Answer) * @see ThrowsMethodExceptionAnswer * @see Mockito#CALLS_REAL_METHODS * @see Mockito#CALLS_REAL_METHODS * * @author Michael Vorburger */ final class CallsRealOrExceptionAnswer implements Answer, Serializable { // intentionally just package local, // make public if there ever is any direct use for this instead through MoreAnswers private static final long serialVersionUID = -3730024662402964588L; static final CallsRealOrExceptionAnswer INSTANCE = new CallsRealOrExceptionAnswer(); private CallsRealOrExceptionAnswer() { } @Override public Object answer(InvocationOnMock invocation) throws Throwable { if (Modifier.isAbstract(invocation.getMethod().getModifiers())) { throw new UnstubbedMethodException(invocation.getMethod(), invocation.getMock()); } return invocation.callRealMethod(); } Object readResolve() { return INSTANCE; } }