/*
* Copyright (c) 2007 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockitousage.annotation;
import org.assertj.core.api.Assertions;
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 java.util.*;
import static junit.framework.TestCase.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
@SuppressWarnings({"unchecked", "unused"})
public class SpyAnnotationTest extends TestBase {
@Spy final List<String> spiedList = new ArrayList<String>();
@Spy NestedClassWithNoArgConstructor staticTypeWithNoArgConstructor;
@Spy
NestedClassWithoutDefinedConstructor 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_prevent_spying_on_interfaces() throws Exception {
class WithSpy {
@Spy List<String> list;
}
WithSpy withSpy = new WithSpy();
try {
MockitoAnnotations.initMocks(withSpy);
fail();
} catch (MockitoException e) {
Assertions.assertThat(e.getMessage()).contains("is an interface and it cannot be spied on");
}
}
@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) {
Assertions.assertThat(e.getMessage()).contains("0-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) {
Assertions.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_encosing_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");
}
}
static class NestedClassWithoutDefinedConstructor { }
static class NestedClassWithNoArgConstructor {
NestedClassWithNoArgConstructor() { }
NestedClassWithNoArgConstructor(String f) { }
}
static class NoValidConstructor {
NoValidConstructor(String f) { }
}
static class ThrowingConstructor {
ThrowingConstructor() { throw new RuntimeException("boo!"); }
}
}