blob: 242382287c58b069f19ecbad984597a0f64f6fad [file] [log] [blame]
/*
* Copyright (c) 2007 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockitousage.annotation;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.exceptions.base.MockitoException;
import org.mockitoutil.TestBase;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertTrue;
import static junit.framework.TestCase.fail;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@SuppressWarnings("unused")
public class SpyAnnotationTest extends TestBase {
@Spy
final List<String> spiedList = new ArrayList<String>();
@Spy
InnerStaticClassWithNoArgConstructor staticTypeWithNoArgConstructor;
@Spy
InnerStaticClassWithoutDefinedConstructor staticTypeWithoutDefinedConstructor;
@Rule
public final ExpectedException shouldThrow = ExpectedException.none();
@Test
public void should_init_spy_by_instance() throws Exception {
doReturn("foo").when(spiedList).get(10);
assertEquals("foo", spiedList.get(10));
assertTrue(spiedList.isEmpty());
}
@Test
public void should_init_spy_and_automatically_create_instance() throws Exception {
when(staticTypeWithNoArgConstructor.toString()).thenReturn("x");
when(staticTypeWithoutDefinedConstructor.toString()).thenReturn("y");
assertEquals("x", staticTypeWithNoArgConstructor.toString());
assertEquals("y", staticTypeWithoutDefinedConstructor.toString());
}
@Test
public void should_allow_spying_on_interfaces() throws Exception {
class WithSpy {
@Spy
List<String> list;
}
WithSpy withSpy = new WithSpy();
MockitoAnnotations.initMocks(withSpy);
when(withSpy.list.size()).thenReturn(3);
assertEquals(3, withSpy.list.size());
}
@Test
public void should_allow_spying_on_interfaces_when_instance_is_concrete() throws Exception {
class WithSpy {
@Spy
List<String> list = new LinkedList<String>();
}
WithSpy withSpy = new WithSpy();
//when
MockitoAnnotations.initMocks(withSpy);
//then
verify(withSpy.list, never()).clear();
}
@Test
public void should_report_when_no_arg_less_constructor() throws Exception {
class FailingSpy {
@Spy
NoValidConstructor noValidConstructor;
}
try {
MockitoAnnotations.initMocks(new FailingSpy());
fail();
} catch (MockitoException e) {
assertThat(e.getMessage()).contains("Please ensure that the type")
.contains(NoValidConstructor.class.getSimpleName())
.contains("has a no-arg constructor");
}
}
@Test
public void should_report_when_constructor_is_explosive() throws Exception {
class FailingSpy {
@Spy
ThrowingConstructor throwingConstructor;
}
try {
MockitoAnnotations.initMocks(new FailingSpy());
fail();
} catch (MockitoException e) {
assertThat(e.getMessage()).contains("Unable to create mock instance");
}
}
@Test
public void should_spy_abstract_class() throws Exception {
class SpyAbstractClass {
@Spy
AbstractList<String> list;
List<String> asSingletonList(String s) {
when(list.size()).thenReturn(1);
when(list.get(0)).thenReturn(s);
return list;
}
}
SpyAbstractClass withSpy = new SpyAbstractClass();
MockitoAnnotations.initMocks(withSpy);
assertEquals(Arrays.asList("a"), withSpy.asSingletonList("a"));
}
@Test
public void should_spy_inner_class() throws Exception {
class WithMockAndSpy {
@Spy
private InnerStrength strength;
@Mock
private List<String> list;
abstract class InnerStrength {
private final String name;
InnerStrength() {
// Make sure that @Mock fields are always injected before @Spy fields.
assertNotNull(list);
// Make sure constructor is indeed called.
this.name = "inner";
}
abstract String strength();
String fullStrength() {
return name + " " + strength();
}
}
}
WithMockAndSpy outer = new WithMockAndSpy();
MockitoAnnotations.initMocks(outer);
when(outer.strength.strength()).thenReturn("strength");
assertEquals("inner strength", outer.strength.fullStrength());
}
@Test(expected = IndexOutOfBoundsException.class)
public void should_reset_spy() throws Exception {
spiedList.get(10); // see shouldInitSpy
}
@Test
public void should_report_when_enclosing_instance_is_needed() throws Exception {
class Outer {
class Inner {
}
}
class WithSpy {
@Spy
private Outer.Inner inner;
}
try {
MockitoAnnotations.initMocks(new WithSpy());
fail();
} catch (MockitoException e) {
assertThat(e).hasMessageContaining("@Spy annotation can only initialize inner classes");
}
}
@Test
public void should_report_private_inner_not_supported() throws Exception {
try {
MockitoAnnotations.initMocks(new WithInnerPrivate());
fail();
} catch (MockitoException e) {
// Currently fails at instantiation time, because the mock subclass don't have the
// 1-arg constructor expected for the outerclass.
// org.mockito.internal.creation.instance.ConstructorInstantiator.withParams()
assertThat(e).hasMessageContaining("Unable to initialize @Spy annotated field 'spy_field'")
.hasMessageContaining(WithInnerPrivate.InnerPrivate.class.getSimpleName());
}
}
@Test
public void should_report_private_abstract_inner_not_supported() throws Exception {
try {
MockitoAnnotations.initMocks(new WithInnerPrivateAbstract());
fail();
} catch (MockitoException e) {
assertThat(e).hasMessageContaining("@Spy annotation can't initialize private abstract inner classes")
.hasMessageContaining(WithInnerPrivateAbstract.class.getSimpleName())
.hasMessageContaining(WithInnerPrivateAbstract.InnerPrivateAbstract.class.getSimpleName())
.hasMessageContaining("You should augment the visibility of this inner class");
}
}
@Test
public void should_report_private_static_abstract_inner_not_supported() throws Exception {
try {
MockitoAnnotations.initMocks(new WithInnerPrivateStaticAbstract());
fail();
} catch (MockitoException e) {
assertThat(e).hasMessageContaining("@Spy annotation can't initialize private abstract inner classes")
.hasMessageContaining(WithInnerPrivateStaticAbstract.class.getSimpleName())
.hasMessageContaining(WithInnerPrivateStaticAbstract.InnerPrivateStaticAbstract.class.getSimpleName())
.hasMessageContaining("You should augment the visibility of this inner class");
}
}
static class WithInnerPrivateStaticAbstract {
@Spy
private InnerPrivateStaticAbstract spy_field;
private static abstract class InnerPrivateStaticAbstract {
}
}
static class WithInnerPrivateAbstract {
@Spy
private InnerPrivateAbstract spy_field;
public void some_method() {
new InnerPrivateConcrete();
}
private abstract class InnerPrivateAbstract {
}
private class InnerPrivateConcrete extends InnerPrivateAbstract {
}
}
static class WithInnerPrivate {
@Spy
private InnerPrivate spy_field;
private class InnerPrivate {
}
private class InnerPrivateSub extends InnerPrivate {}
}
static class InnerStaticClassWithoutDefinedConstructor {
}
static class InnerStaticClassWithNoArgConstructor {
InnerStaticClassWithNoArgConstructor() {
}
InnerStaticClassWithNoArgConstructor(String f) {
}
}
static class NoValidConstructor {
NoValidConstructor(String f) {
}
}
static class ThrowingConstructor {
ThrowingConstructor() {
throw new RuntimeException("boo!");
}
}
}