Home | History | Annotate | Download | only in annotation
      1 /*
      2  * Copyright (c) 2007 Mockito contributors
      3  * This program is made available under the terms of the MIT License.
      4  */
      5 package org.mockitousage.annotation;
      6 
      7 import java.util.AbstractList;
      8 import java.util.ArrayList;
      9 import java.util.Arrays;
     10 import java.util.LinkedList;
     11 import java.util.List;
     12 import org.junit.Rule;
     13 import org.junit.Test;
     14 import org.junit.rules.ExpectedException;
     15 import org.mockito.Mock;
     16 import org.mockito.Mockito;
     17 import org.mockito.MockitoAnnotations;
     18 import org.mockito.Spy;
     19 import org.mockito.exceptions.base.MockitoException;
     20 import org.mockitoutil.TestBase;
     21 
     22 import static org.junit.Assert.assertEquals;
     23 import static org.junit.Assert.assertNotNull;
     24 import static org.junit.Assert.assertTrue;
     25 import static org.junit.Assert.fail;
     26 import static org.assertj.core.api.Assertions.assertThat;
     27 import static org.mockito.Mockito.doReturn;
     28 import static org.mockito.Mockito.eq;
     29 import static org.mockito.Mockito.never;
     30 import static org.mockito.Mockito.verify;
     31 import static org.mockito.Mockito.when;
     32 
     33 @SuppressWarnings("unused")
     34 public class SpyAnnotationTest extends TestBase {
     35 
     36     @Spy
     37     final List<String> spiedList = new ArrayList<String>();
     38 
     39     @Spy
     40     InnerStaticClassWithNoArgConstructor staticTypeWithNoArgConstructor;
     41 
     42     @Spy
     43     InnerStaticClassWithoutDefinedConstructor staticTypeWithoutDefinedConstructor;
     44 
     45     @Spy
     46     MockTranslator translator;
     47 
     48     @Rule
     49     public final ExpectedException shouldThrow = ExpectedException.none();
     50 
     51     @Test
     52     public void should_init_spy_by_instance() throws Exception {
     53         doReturn("foo").when(spiedList).get(10);
     54         assertEquals("foo", spiedList.get(10));
     55         assertTrue(spiedList.isEmpty());
     56     }
     57 
     58     @Test
     59     public void should_init_spy_and_automatically_create_instance() throws Exception {
     60         when(staticTypeWithNoArgConstructor.toString()).thenReturn("x");
     61         when(staticTypeWithoutDefinedConstructor.toString()).thenReturn("y");
     62         assertEquals("x", staticTypeWithNoArgConstructor.toString());
     63         assertEquals("y", staticTypeWithoutDefinedConstructor.toString());
     64     }
     65 
     66     @Test
     67     public void should_allow_spying_on_interfaces() throws Exception {
     68         class WithSpy {
     69             @Spy
     70             List<String> list;
     71         }
     72 
     73         WithSpy withSpy = new WithSpy();
     74         MockitoAnnotations.initMocks(withSpy);
     75         when(withSpy.list.size()).thenReturn(3);
     76         assertEquals(3, withSpy.list.size());
     77     }
     78 
     79     @Test
     80     public void should_allow_spying_on_interfaces_when_instance_is_concrete() throws Exception {
     81         class WithSpy {
     82             @Spy
     83             List<String> list = new LinkedList<String>();
     84         }
     85         WithSpy withSpy = new WithSpy();
     86 
     87         //when
     88         MockitoAnnotations.initMocks(withSpy);
     89 
     90         //then
     91         verify(withSpy.list, never()).clear();
     92     }
     93 
     94     @Test
     95     public void should_report_when_no_arg_less_constructor() throws Exception {
     96         class FailingSpy {
     97             @Spy
     98             NoValidConstructor noValidConstructor;
     99         }
    100 
    101         try {
    102             MockitoAnnotations.initMocks(new FailingSpy());
    103             fail();
    104         } catch (MockitoException e) {
    105             assertThat(e.getMessage()).contains("Please ensure that the type")
    106                     .contains(NoValidConstructor.class.getSimpleName())
    107                     .contains("has a no-arg constructor");
    108         }
    109     }
    110 
    111     @Test
    112     public void should_report_when_constructor_is_explosive() throws Exception {
    113         class FailingSpy {
    114             @Spy
    115             ThrowingConstructor throwingConstructor;
    116         }
    117 
    118         try {
    119             MockitoAnnotations.initMocks(new FailingSpy());
    120             fail();
    121         } catch (MockitoException e) {
    122             assertThat(e.getMessage()).contains("Unable to create mock instance");
    123         }
    124     }
    125 
    126     @Test
    127     public void should_spy_abstract_class() throws Exception {
    128         class SpyAbstractClass {
    129             @Spy
    130             AbstractList<String> list;
    131 
    132             List<String> asSingletonList(String s) {
    133                 when(list.size()).thenReturn(1);
    134                 when(list.get(0)).thenReturn(s);
    135                 return list;
    136             }
    137         }
    138         SpyAbstractClass withSpy = new SpyAbstractClass();
    139         MockitoAnnotations.initMocks(withSpy);
    140         assertEquals(Arrays.asList("a"), withSpy.asSingletonList("a"));
    141     }
    142 
    143     @Test
    144     public void should_spy_inner_class() throws Exception {
    145 
    146         class WithMockAndSpy {
    147             @Spy
    148             private InnerStrength strength;
    149             @Mock
    150             private List<String> list;
    151 
    152             abstract class InnerStrength {
    153                 private final String name;
    154 
    155                 InnerStrength() {
    156                     // Make sure that @Mock fields are always injected before @Spy fields.
    157                     assertNotNull(list);
    158                     // Make sure constructor is indeed called.
    159                     this.name = "inner";
    160                 }
    161 
    162                 abstract String strength();
    163 
    164                 String fullStrength() {
    165                     return name + " " + strength();
    166                 }
    167             }
    168         }
    169         WithMockAndSpy outer = new WithMockAndSpy();
    170         MockitoAnnotations.initMocks(outer);
    171         when(outer.strength.strength()).thenReturn("strength");
    172         assertEquals("inner strength", outer.strength.fullStrength());
    173     }
    174 
    175     @Test(expected = IndexOutOfBoundsException.class)
    176     public void should_reset_spy() throws Exception {
    177         spiedList.get(10); // see shouldInitSpy
    178     }
    179 
    180     @Test
    181     public void should_report_when_enclosing_instance_is_needed() throws Exception {
    182         class Outer {
    183             class Inner {
    184             }
    185         }
    186         class WithSpy {
    187             @Spy
    188             private Outer.Inner inner;
    189         }
    190         try {
    191             MockitoAnnotations.initMocks(new WithSpy());
    192             fail();
    193         } catch (MockitoException e) {
    194             assertThat(e).hasMessageContaining("@Spy annotation can only initialize inner classes");
    195         }
    196     }
    197 
    198     @Test
    199     public void should_report_private_inner_not_supported() throws Exception {
    200         try {
    201             MockitoAnnotations.initMocks(new WithInnerPrivate());
    202             fail();
    203         } catch (MockitoException e) {
    204             // Currently fails at instantiation time, because the mock subclass don't have the
    205             // 1-arg constructor expected for the outerclass.
    206             // org.mockito.internal.creation.instance.ConstructorInstantiator.withParams()
    207             assertThat(e).hasMessageContaining("Unable to initialize @Spy annotated field 'spy_field'")
    208                     .hasMessageContaining(WithInnerPrivate.InnerPrivate.class.getSimpleName());
    209         }
    210     }
    211 
    212     @Test
    213     public void should_report_private_abstract_inner_not_supported() throws Exception {
    214         try {
    215             MockitoAnnotations.initMocks(new WithInnerPrivateAbstract());
    216             fail();
    217         } catch (MockitoException e) {
    218             assertThat(e).hasMessageContaining("@Spy annotation can't initialize private abstract inner classes")
    219                     .hasMessageContaining(WithInnerPrivateAbstract.class.getSimpleName())
    220                     .hasMessageContaining(WithInnerPrivateAbstract.InnerPrivateAbstract.class.getSimpleName())
    221                     .hasMessageContaining("You should augment the visibility of this inner class");
    222         }
    223     }
    224 
    225     @Test
    226     public void should_report_private_static_abstract_inner_not_supported() throws Exception {
    227         try {
    228             MockitoAnnotations.initMocks(new WithInnerPrivateStaticAbstract());
    229             fail();
    230         } catch (MockitoException e) {
    231             assertThat(e).hasMessageContaining("@Spy annotation can't initialize private abstract inner classes")
    232                     .hasMessageContaining(WithInnerPrivateStaticAbstract.class.getSimpleName())
    233                     .hasMessageContaining(WithInnerPrivateStaticAbstract.InnerPrivateStaticAbstract.class.getSimpleName())
    234                     .hasMessageContaining("You should augment the visibility of this inner class");
    235         }
    236     }
    237 
    238     @Test
    239     public void should_be_able_to_stub_and_verify_via_varargs_for_list_params() throws Exception {
    240       // You can stub with varargs.
    241       when(translator.translate("hello", "mockito")).thenReturn(Arrays.asList("you", "too"));
    242 
    243       // Pretend the prod code will call translate(List<String>) with these elements.
    244       assertThat(translator.translate(Arrays.asList("hello", "mockito"))).containsExactly("you", "too");
    245       assertThat(translator.translate(Arrays.asList("not stubbed"))).isEmpty();
    246 
    247       // You can verify with varargs.
    248       verify(translator).translate("hello", "mockito");
    249     }
    250 
    251     @Test
    252     public void should_be_able_to_stub_and_verify_via_varargs_of_matchers_for_list_params() throws Exception {
    253       // You can stub with varargs of matchers.
    254       when(translator.translate(Mockito.anyString())).thenReturn(Arrays.asList("huh?"));
    255       when(translator.translate(eq("hello"))).thenReturn(Arrays.asList("hi"));
    256 
    257       // Pretend the prod code will call translate(List<String>) with these elements.
    258       assertThat(translator.translate(Arrays.asList("hello"))).containsExactly("hi");
    259       assertThat(translator.translate(Arrays.asList("not explicitly stubbed"))).containsExactly("huh?");
    260 
    261       // You can verify with varargs of matchers.
    262       verify(translator).translate(eq("hello"));
    263     }
    264 
    265     static class WithInnerPrivateStaticAbstract {
    266         @Spy
    267         private InnerPrivateStaticAbstract spy_field;
    268 
    269         private static abstract class InnerPrivateStaticAbstract {
    270         }
    271     }
    272     static class WithInnerPrivateAbstract {
    273         @Spy
    274         private InnerPrivateAbstract spy_field;
    275 
    276         public void some_method() {
    277             new InnerPrivateConcrete();
    278         }
    279 
    280         private abstract class InnerPrivateAbstract {
    281         }
    282 
    283         private class InnerPrivateConcrete extends InnerPrivateAbstract {
    284 
    285         }
    286     }
    287 
    288     static class WithInnerPrivate {
    289         @Spy
    290         private InnerPrivate spy_field;
    291 
    292         private class InnerPrivate {
    293         }
    294 
    295         private class InnerPrivateSub extends InnerPrivate {}
    296     }
    297 
    298     static class InnerStaticClassWithoutDefinedConstructor {
    299     }
    300 
    301     static class InnerStaticClassWithNoArgConstructor {
    302         InnerStaticClassWithNoArgConstructor() {
    303         }
    304 
    305         InnerStaticClassWithNoArgConstructor(String f) {
    306         }
    307     }
    308 
    309     static class NoValidConstructor {
    310         NoValidConstructor(String f) {
    311         }
    312     }
    313 
    314     static class ThrowingConstructor {
    315         ThrowingConstructor() {
    316             throw new RuntimeException("boo!");
    317         }
    318     }
    319 
    320     interface Translator {
    321       List<String> translate(List<String> messages);
    322     }
    323 
    324     static abstract class MockTranslator implements Translator {
    325       @Override public final List<String> translate(List<String> messages) {
    326         return translate(messages.toArray(new String[0]));
    327       }
    328 
    329       abstract List<String> translate(String... messages);
    330     }
    331 }
    332