Intro. new yangtools.testutils artifacts (incl. Mikito) 29/42529/8
authorMichael Vorburger <vorburger@redhat.com>
Tue, 26 Jul 2016 03:46:08 +0000 (05:46 +0200)
committerMichael Vorburger <vorburger@redhat.com>
Wed, 10 Aug 2016 14:38:20 +0000 (16:38 +0200)
Seeding new project with proposed Mikito (new), and a
MockitoUnstubbedMethodExceptionAnswer (alternative to
yangtools.mockito-configuration; see the package-info.java in
mockito-configuration about why this is not being proposed in that
project).

Change-Id: I78237936924e0befc0ff17e7604e0fb33262d7cd
Signed-off-by: Michael Vorburger <vorburger@redhat.com>
13 files changed:
common/artifacts/pom.xml
common/pom.xml
common/testutils/pom.xml [new file with mode: 0644]
common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/CallsRealOrExceptionAnswer.java [new file with mode: 0644]
common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/MethodExtensions.java [new file with mode: 0644]
common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/MoreAnswers.java [new file with mode: 0644]
common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/ThrowsMethodExceptionAnswer.java [new file with mode: 0644]
common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/UnstubbedMethodException.java [new file with mode: 0644]
common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MethodExtensionsTest.java [new file with mode: 0644]
common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MikitoTest.java [new file with mode: 0644]
common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MockitoExampleTutorialTest.java [new file with mode: 0644]
common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MockitoUnstubbedMethodExceptionAnswerTest.java [new file with mode: 0644]
common/util/pom.xml

index c24871f06e12ddea0876bc3666809cc6c00eec1a..d9d64f7d9d54ab1913b85b999053591402de10a2 100644 (file)
                 <version>${project.version}</version>
                 <scope>test</scope>
             </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>testutils</artifactId>
+                <version>${project.version}</version>
+                <scope>test</scope>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 
index 03ac840b613ae41ced5a0aa980d21fa2bf2db584..e207bcbeeff6367814211b356012e1210112a584 100644 (file)
@@ -32,6 +32,7 @@
         <module>object-cache-guava</module>
         <module>object-cache-noop</module>
         <module>util</module>
+        <module>testutils</module>
     </modules>
 
   <!--
diff --git a/common/testutils/pom.xml b/common/testutils/pom.xml
new file mode 100644 (file)
index 0000000..13647dc
--- /dev/null
@@ -0,0 +1,76 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+      <groupId>org.opendaylight.odlparent</groupId>
+      <artifactId>bundle-parent</artifactId>
+      <version>1.8.0-SNAPSHOT</version>
+      <relativePath/>
+  </parent>
+
+  <groupId>org.opendaylight.yangtools</groupId>
+  <artifactId>testutils</artifactId>
+  <version>1.1.0-SNAPSHOT</version>
+  <!-- Currently not needed, maybe later: <packaging>bundle</packaging> -->
+
+  <dependencyManagement>
+      <dependencies>
+          <dependency>
+              <groupId>org.opendaylight.yangtools</groupId>
+              <artifactId>yangtools-artifacts</artifactId>
+              <version>${project.version}</version>
+              <scope>import</scope>
+              <type>pom</type>
+          </dependency>
+      </dependencies>
+  </dependencyManagement>
+
+  <dependencies>
+    <!-- NOTE: The use of <scope> here is a little particular, compared to other standard projects...
+
+        As this test helper project is intended to itself be used as a <scope>test
+        <dependency> so that the utility code in src/main/java of this project can
+        be used to write src/test/java code in projects using it, all <dependencies>
+        here are <scope>compile here (the default, don't mention it), and NOT <scope>test.
+        (Only a <dependency> which only this project would want to use in its own src/test/java code
+        but not expose to projects depending on it would be <scope>test.  However that kind of against
+        the whole point of this project, and currently there no such dependencies here.
+      -->
+    <dependency>
+        <groupId>org.slf4j</groupId>
+        <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-simple</artifactId>
+    </dependency>
+    <dependency>
+        <groupId>junit</groupId>
+        <artifactId>junit</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.google.truth</groupId>
+      <artifactId>truth</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.xtend</groupId>
+      <artifactId>org.eclipse.xtend.lib</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.eclipse.xtend</groupId>
+        <artifactId>xtend-maven-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/CallsRealOrExceptionAnswer.java b/common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/CallsRealOrExceptionAnswer.java
new file mode 100644 (file)
index 0000000..9426fd3
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * 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&lt;Thing&gt; 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&lt;Thing&gt; things = new ArrayList&lt;&gt;();
+ *
+ *     public List&lt;Thing&gt; getThings() {
+ *         return things;
+ *     }
+ *
+ *     &#64;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&lt;Thing&gt; things;
+ *
+ *     public List&lt;Thing&gt; getThings() {
+ *         if (things == null)
+ *             things = new ArrayList&lt;&gt;()
+ *         return things;
+ *     }
+ *
+ *     &#64;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();
+    }
+}
diff --git a/common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/MethodExtensions.java b/common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/MethodExtensions.java
new file mode 100644 (file)
index 0000000..c81bcc0
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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 java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.lang.reflect.Type;
+
+/**
+ * Nicer shorter toString() for {@link Method} than it's default.
+ *
+ * <p>Without modifiers, return type, FQN of class and exceptions; instead with parameter names (if javac -parameters).
+ *
+ * @author Michael Vorburger
+ */
+public final class MethodExtensions {
+
+    private MethodExtensions() {
+    }
+
+    public static String toString(Method method) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(method.getName());
+
+        // copy/paste from java.lang.reflect.Executable.sharedToGenericString(int, boolean)
+        sb.append('(');
+        Type[] params = method.getGenericParameterTypes();
+        Parameter[] parameters = method.getParameters(); // NEW
+        for (int j = 0; j < params.length; j++) {
+            String param = params[j].getTypeName();
+            if (method.isVarArgs() && j == params.length - 1) { // replace T[] with T...
+                param = param.replaceFirst("\\[\\]$", "...");
+            }
+            sb.append(param);
+            // NEW
+            if (parameters[j].isNamePresent()) {
+                sb.append(' ');
+                sb.append(parameters[j].getName());
+            }
+            // NEW END
+            if (j < (params.length - 1)) {
+                sb.append(", "); // NEW ", " instead of ','
+            }
+        }
+        sb.append(')');
+
+        return sb.toString();
+    }
+
+}
diff --git a/common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/MoreAnswers.java b/common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/MoreAnswers.java
new file mode 100644 (file)
index 0000000..ce0089b
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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 org.mockito.AdditionalAnswers;
+import org.mockito.Answers;
+import org.mockito.stubbing.Answer;
+
+/**
+ * More {@link Answer} variants.
+ *
+ * @see Answers
+ * @see AdditionalAnswers
+ *
+ * @author Michael Vorburger
+ */
+@SuppressWarnings("unchecked")
+public final class MoreAnswers {
+
+    private static final CallsRealOrExceptionAnswer REAL_OR_EXCEPTION
+        = new CallsRealOrExceptionAnswer();
+
+    private static final ThrowsMethodExceptionAnswer EXCEPTION
+        = new ThrowsMethodExceptionAnswer();
+
+    private MoreAnswers() {
+    }
+
+    /**
+     * Returns Mockito Answer (default) which forwards method calls or throws an UnstubbedMethodException.
+     *
+     * @see CallsRealOrExceptionAnswer
+     */
+    public static <T> Answer<T> realOrException() {
+        return (Answer<T>) REAL_OR_EXCEPTION;
+    }
+
+    /**
+     * Returns Mockito Answer (default) which throws an UnstubbedMethodException.
+     *
+     * @see ThrowsMethodExceptionAnswer
+     */
+    public static <T> Answer<T> exception() {
+        return (Answer<T>) EXCEPTION;
+    }
+
+}
diff --git a/common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/ThrowsMethodExceptionAnswer.java b/common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/ThrowsMethodExceptionAnswer.java
new file mode 100644 (file)
index 0000000..f5af1bf
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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 org.mockito.Mockito;
+import org.mockito.internal.stubbing.answers.ThrowsException;
+import org.mockito.internal.stubbing.answers.ThrowsExceptionClass;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ * Mockito Answer which for un-stubbed methods throws an
+ * UnstubbedMethodException (instead of Mockito's default of returning null).
+ *
+ * <p>
+ * Usage:
+ *
+ * <pre>
+ * import static ...testutils.mockito.MoreAnswers.exception;
+ *
+ * Mockito.mock(YourInterface.class, exception())
+ * </pre>
+ *
+ * @see Mockito#mock(Class, Answer)
+ *
+ * @see ThrowsException
+ * @see ThrowsExceptionClass
+ *
+ * @author Michael Vorburger
+ */
+@Beta
+public class ThrowsMethodExceptionAnswer implements Answer<Object>, Serializable {
+    private static final long serialVersionUID = -7316574192253912318L;
+
+    /**
+     * Use {@link MoreAnswers} to obtain an instance.
+     */
+    ThrowsMethodExceptionAnswer() {
+    }
+
+    @Override
+    public Void answer(InvocationOnMock invocation) throws Throwable {
+        throw new UnstubbedMethodException(invocation.getMethod());
+    }
+
+}
diff --git a/common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/UnstubbedMethodException.java b/common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/UnstubbedMethodException.java
new file mode 100644 (file)
index 0000000..5e84a02
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2016 Red Hat 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 java.lang.reflect.Method;
+import org.mockito.internal.util.MockUtil;
+
+/**
+ * Exception to be thrown on unstubbed method calls.
+ *
+ * @author Michael Vorburger
+ */
+public class UnstubbedMethodException extends UnsupportedOperationException {
+    private static final long serialVersionUID = 1L;
+
+    public UnstubbedMethodException(Method method) {
+        super(MethodExtensions.toString(method) + " is not stubbed in mock of " + method.getDeclaringClass().getName());
+    }
+
+    public UnstubbedMethodException(Method method, Object mockAbstractFakeObject) {
+        super(MethodExtensions.toString(method) + " is not implemented in "
+                + new MockUtil().getMockName(mockAbstractFakeObject).toString());
+    }
+}
diff --git a/common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MethodExtensionsTest.java b/common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MethodExtensionsTest.java
new file mode 100644 (file)
index 0000000..a48538c
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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.tests;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import java.lang.reflect.Method;
+import org.junit.Test;
+import org.opendaylight.yangtools.testutils.mockito.MethodExtensions;
+
+public class MethodExtensionsTest {
+
+    public <T> void fooBar(int index, T element) {
+    }
+
+    @Test
+    public void betterToString() throws Exception {
+        Method method = MethodExtensionsTest.class.getMethod("fooBar", Integer.TYPE, Object.class);
+        assertThat(MethodExtensions.toString(method)).isEqualTo("fooBar(int index, T element)");
+    }
+
+}
diff --git a/common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MikitoTest.java b/common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MikitoTest.java
new file mode 100644 (file)
index 0000000..7708a87
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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.tests;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.testutils.mockito.MoreAnswers.realOrException;
+
+import java.io.File;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.yangtools.testutils.mockito.UnstubbedMethodException;
+
+/**
+ * Test to illustrate the use of the REAL_OR_EXCEPTION.
+ *
+ * <p>Also useful as example to contrast this approach illustrated in the MockitoExampleTutorialTest.
+ *
+ * @see MockitoExampleTutorialTest
+ *
+ * @author Michael Vorburger
+ */
+public class MikitoTest {
+
+    interface SomeService {
+
+        void foo();
+
+        String bar(String arg);
+
+        // Most methods on real world services have complex input (and output objects), not just int or String
+        int foobar(File file);
+    }
+
+    @Test
+    public void usingMikitoToCallStubbedMethod() {
+        SomeService service = Mockito.mock(MockSomeService.class, realOrException());
+        assertEquals(123, service.foobar(new File("hello.txt")));
+        assertEquals(0, service.foobar(new File("belo.txt")));
+    }
+
+    @Test
+    public void usingMikitoToCallUnstubbedMethodAndExpectException() {
+        MockSomeService service = Mockito.mock(MockSomeService.class, realOrException());
+        try {
+            service.foo();
+            fail();
+        } catch (UnstubbedMethodException e) {
+            assertThat(e.getMessage()).isEqualTo("foo() is not implemented in mockSomeService");
+        }
+    }
+
+    abstract static class MockSomeService implements SomeService {
+        @Override
+        public int foobar(File file) {
+            if (file.getName().equals("hello.txt")) {
+                return 123;
+            } else {
+                return 0;
+            }
+        }
+    }
+
+}
diff --git a/common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MockitoExampleTutorialTest.java b/common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MockitoExampleTutorialTest.java
new file mode 100644 (file)
index 0000000..0896717
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * 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.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.opendaylight.yangtools.testutils.mockito.MoreAnswers.exception;
+
+import java.io.File;
+import org.junit.Test;
+import org.opendaylight.yangtools.testutils.mockito.UnstubbedMethodException;
+
+/**
+ * Test to illustrate the basic use of Mockito VS the EXCEPTION_ANSWER.
+ *
+ * <p>Also useful as example to contrast this approach with the REAL_OR_EXCEPTION
+ * approach illustrated in the MikitoTest.
+ *
+ * @see MikitoTest
+ *
+ * @author Michael Vorburger
+ */
+public class MockitoExampleTutorialTest {
+
+    interface SomeService {
+
+        void foo();
+
+        String bar(String arg);
+
+        // Most methods on real world services have complex input (and output objects), not just int or String
+        int foobar(File file);
+    }
+
+    @Test
+    public void usingMockitoWithoutStubbing() {
+        SomeService service = mock(SomeService.class);
+        assertNull(service.bar("hulo"));
+    }
+
+    @Test
+    public void usingMockitoToStubSimpleCase() {
+        SomeService service = mock(SomeService.class);
+        when(service.foobar(any())).thenReturn(123);
+        assertEquals(123, service.foobar(new File("hello.txt")));
+    }
+
+    @Test
+    public void usingMockitoToStubComplexCase() {
+        SomeService service = mock(SomeService.class);
+        when(service.foobar(any())).thenAnswer(invocation -> {
+            // Urgh! This is ugly.. (Mockito 2.0 may be better, see http://site.mockito.org/mockito/docs/current/org/mockito/ArgumentMatcher.html)
+            File file = (File) invocation.getArgumentAt(0, File.class);
+            if (file.getName().equals("hello.txt")) {
+                return 123;
+            } else {
+                return 0;
+            }
+        });
+        assertEquals(0, service.foobar(new File("belo.txt")));
+    }
+
+    @Test(expected = UnstubbedMethodException.class)
+    public void usingMockitoExceptionException() {
+        SomeService service = mock(SomeService.class, exception());
+        service.foo();
+    }
+
+    @Test
+    public void usingMockitoNoExceptionIfStubbed() {
+        SomeService service = mock(SomeService.class, exception());
+        // NOT when(s.foobar(any())).thenReturn(123) BUT must be like this:
+        doReturn(123).when(service).foobar(any());
+        assertEquals(123, service.foobar(new File("hello.txt")));
+        try {
+            service.foo();
+            fail("expected NotImplementedException");
+        } catch (UnstubbedMethodException e) {
+            // OK
+        }
+    }
+
+    @Test
+    public void usingMockitoToStubComplexCaseAndExceptionIfNotStubbed() {
+        SomeService service = mock(SomeService.class, exception());
+        doAnswer(invocation -> {
+            // Urgh! This is ugly. Mockito may be better, see http://site.mockito.org/mockito/docs/current/org/mockito/ArgumentMatcher.html
+            File file = (File) invocation.getArguments()[0];
+            if (file.getName().equals("hello.txt")) {
+                return 123;
+            } else {
+                return 0;
+            }
+        }).when(service).foobar(any());
+        assertEquals(123, service.foobar(new File("hello.txt")));
+        assertEquals(0, service.foobar(new File("belo.txt")));
+    }
+
+}
diff --git a/common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MockitoUnstubbedMethodExceptionAnswerTest.java b/common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MockitoUnstubbedMethodExceptionAnswerTest.java
new file mode 100644 (file)
index 0000000..2b1296f
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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.tests;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.testutils.mockito.MoreAnswers.exception;
+
+import java.io.Closeable;
+import java.io.IOException;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.yangtools.testutils.mockito.UnstubbedMethodException;
+
+public class MockitoUnstubbedMethodExceptionAnswerTest {
+
+    @Test
+    public void testAnswering() throws IOException {
+        Closeable mock = Mockito.mock(Closeable.class, exception());
+        try {
+            mock.close();
+            fail();
+        } catch (UnstubbedMethodException e) {
+            assertThat(e.getMessage()).isEqualTo("close() is not stubbed in mock of java.io.Closeable");
+        }
+
+    }
+
+}
index 93f1b9329edb79de85a3d2c037e5de9157bd3a5a..f3e71bb582ab6a71b303f97186878f48bc5842bc 100644 (file)
@@ -26,7 +26,7 @@
             <dependency>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yangtools-artifacts</artifactId>
-                <version>1.1.0-SNAPSHOT</version>
+                <version>${project.version}</version>
                 <scope>import</scope>
                 <type>pom</type>
             </dependency>