/*
 * Copyright (c) 2007 Mockito contributors
 * This program is made available under the terms of the MIT License.
 */
package org.mockitousage.spies;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.*;
import static org.junit.Assume.assumeTrue;
import static org.mockito.Mockito.*;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.mockito.exceptions.base.MockitoException;
import org.mockito.exceptions.verification.NoInteractionsWanted;
import org.mockito.exceptions.verification.TooFewActualInvocations;
import org.mockito.exceptions.verification.VerificationInOrderFailure;
import org.mockitoutil.TestBase;

public class SpyingOnRealObjectsTest extends TestBase {

    List<String> list = new LinkedList<String>();
    List<String> spy = Mockito.spy(list);

    @Test
    public void shouldVerify() {
        spy.add("one");
        spy.add("two");

        assertEquals("one", spy.get(0));
        assertEquals("two", spy.get(1));

        verify(spy).add("one");
        verify(spy).add("two");
    }

    @SuppressWarnings({"CheckReturnValue", "MockitoUsage"})
    @Test
    public void shouldBeAbleToMockObjectBecauseWhyNot() {
        spy(new Object());
    }

    @Test
    public void shouldStub() {
        spy.add("one");
        when(spy.get(0)).thenReturn("1").thenReturn("1 again");

        assertEquals("1", spy.get(0));
        assertEquals("1 again", spy.get(0));
        assertEquals("one", spy.iterator().next());

        assertEquals(1, spy.size());
    }

    @Test
    public void shouldAllowOverridingStubs() {
        when(spy.contains(anyObject())).thenReturn(true);
        when(spy.contains("foo")).thenReturn(false);

        assertTrue(spy.contains("bar"));
        assertFalse(spy.contains("foo"));
    }

    @Test
    public void shouldStubVoid() {
        doNothing().doThrow(new RuntimeException()).when(spy).clear();

        spy.add("one");
        spy.clear();
        try {
            spy.clear();
            fail();
        } catch (RuntimeException e) {
        }

        assertEquals(1, spy.size());
    }

    @Test
    public void shouldStubWithDoReturnAndVerify() {
        doReturn("foo").doReturn("bar").when(spy).get(0);

        assertEquals("foo", spy.get(0));
        assertEquals("bar", spy.get(0));

        verify(spy, times(2)).get(0);
        verifyNoMoreInteractions(spy);
    }

    @Test
    public void shouldVerifyInOrder() {
        spy.add("one");
        spy.add("two");

        InOrder inOrder = inOrder(spy);
        inOrder.verify(spy).add("one");
        inOrder.verify(spy).add("two");

        verifyNoMoreInteractions(spy);
    }

    @Test
    public void shouldVerifyInOrderAndFail() {
        spy.add("one");
        spy.add("two");

        InOrder inOrder = inOrder(spy);
        inOrder.verify(spy).add("two");
        try {
            inOrder.verify(spy).add("one");
            fail();
        } catch (VerificationInOrderFailure f) {
        }
    }

    @Test
    public void shouldVerifyNumberOfTimes() {
        spy.add("one");
        spy.add("one");

        verify(spy, times(2)).add("one");
        verifyNoMoreInteractions(spy);
    }

    @Test
    public void shouldVerifyNumberOfTimesAndFail() {
        spy.add("one");
        spy.add("one");

        try {
            verify(spy, times(3)).add("one");
            fail();
        } catch (TooFewActualInvocations e) {
        }
    }

    @Test
    public void shouldVerifyNoMoreInteractionsAndFail() {
        spy.add("one");
        spy.add("two");

        verify(spy).add("one");
        try {
            verifyNoMoreInteractions(spy);
            fail();
        } catch (NoInteractionsWanted e) {
        }
    }

    @Test
    public void shouldToString() {
        spy.add("foo");
        assertEquals("[foo]", spy.toString());
    }

    interface Foo {
        String print();
    }

    @Test
    public void shouldAllowSpyingAnonymousClasses() {
        // when
        Foo spy =
                spy(
                        new Foo() {
                            public String print() {
                                return "foo";
                            }
                        });

        // then
        assertEquals("foo", spy.print());
    }

    @Test
    public void shouldSayNiceMessageWhenSpyingOnPrivateClass() throws Exception {
        List<String> real = Arrays.asList("first", "second");
        try {
            List<String> spy = spy(real);
            assumeTrue(
                    "Using inline mocks, it is possible to spy on private types",
                    spy.getClass() != real.getClass());
            fail();
        } catch (MockitoException e) {
            assertThat(e)
                    .hasMessageContaining(
                            "Most likely it is due to mocking a private class that is not visible to Mockito");
        }
    }
}

@@ -1722,22 +1722,16 @@
 ains(any
-Object
 ())).the

Remove deprecation warning