/* * 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.yangtools.testutils.mockito; import com.google.common.annotations.Beta; 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}. * * <p> * This can be useful to create light-weight <a href= * "http://googletesting.blogspot.ch/2013/07/testing-on-toilet-know-your-test-doubles.html">Fake Doubles</a> * (in particular some with state). For example: * * <pre> * 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()) * </pre> * * <p> * 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: * * <pre> * 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); * } * } * </pre> * * <p> * you'll just need to do: * * <pre> * 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); * } * } * </pre> * * <p> * The big advantage of Mikitos 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. * * <p> * The advantage of Mikitos VS pure Mockito's when/thenAnswer are that they: * <ul> * * <li>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.</li> * <li>avoid confusion re. the alternative doReturn(...).when() syntax required * with ThrowsMethodExceptionAnswer instead of when(...).thenReturn()</li> * <li>enforce the ThrowsMethodExceptionAnswer by default for * non-implemented methods (which is possible with Mockito by explicitly passing * this, but is easily forgotten)</li> * </ul> * * @see Mockito#mock(Class, Answer) * @see ThrowsMethodExceptionAnswer * @see Mockito#CALLS_REAL_METHODS * @see Mockito#CALLS_REAL_METHODS * * @author Michael Vorburger */ @Beta public class CallsRealOrExceptionAnswer implements Answer<Object>, Serializable { private static final long serialVersionUID = -3730024662402964588L; /** * Use {@link MoreAnswers} to obtain an instance. */ 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(); } }