// Copyright © 2011-2013, Esko Luontola <www.orfjackal.net> // This software is released under the Apache License 2.0. // The license text is at http://www.apache.org/licenses/LICENSE-2.0 package fi.jumi.core.util; import org.junit.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; public class SpyListenerTest { private final SpyListener<DummyListener> spy = new SpyListener<>(DummyListener.class); private final DummyListener listener = spy.getListener(); @Test public void no_expectations() { spy.replay(); passes(); } @Test public void matching_expectations() { listener.onFirst(); listener.onSecond(); spy.replay(); listener.onFirst(); listener.onSecond(); passes(); } // TODO: write as white-box unit tests, instead of two "POS" and "NEG" test methods @Test public void throwable_arguments_are_matched_by_type_and_message_POS() { listener.onException(new IllegalArgumentException("foo")); spy.replay(); listener.onException(new IllegalArgumentException("foo")); passes(); } @Test public void throwable_arguments_are_matched_by_type_and_message_NEG() { listener.onException(new IllegalArgumentException("foo")); spy.replay(); listener.onException(new IllegalArgumentException("bar")); fails(); } @Test public void fails_if_wrong_method_is_called() { listener.onFirst(); spy.replay(); listener.onSecond(); fails(); } @Test public void fails_if_right_method_is_called_with_wrong_parameters() { listener.onParameter(1); spy.replay(); listener.onParameter(2); fails(); } @Test public void fails_if_right_calls_were_made_in_wrong_order() { listener.onFirst(); listener.onSecond(); spy.replay(); listener.onSecond(); listener.onFirst(); fails(); } @Test public void fails_if_there_were_fewer_calls_than_expected() { listener.onFirst(); listener.onSecond(); spy.replay(); listener.onFirst(); fails(); } @Test public void fails_if_there_were_more_calls_than_expected() { listener.onFirst(); listener.onSecond(); spy.replay(); listener.onFirst(); listener.onSecond(); listener.onSecond(); fails(); } @Test public void fails_if_an_overloaded_version_of_the_expected_method_was_called() { listener.overloadedMethod(1); spy.replay(); listener.overloadedMethod(1, 2); fails(); } @Test public void failure_messages_contain_an_ordered_list_of_all_expected_method_calls() { listener.onFirst(); listener.onSecond(); spy.replay(); String message = fails(); assertThat(message, containsString("1. onFirst()")); assertThat(message, containsString("2. onSecond()")); } @Test public void failure_messages_contain_an_ordered_list_of_all_actual_method_calls() { spy.replay(); listener.onFirst(); listener.onSecond(); String message = fails(); assertThat(message, containsString("1. onFirst()")); assertThat(message, containsString("2. onSecond()")); } @Test public void method_calls_show_arguments_and_string_arguments_are_escaped() { spy.replay(); listener.onParameter(123); listener.onStringParameter("foobar\n"); String message = fails(); assertThat(message, containsString("1. onParameter(123)")); assertThat(message, containsString("2. onStringParameter(\"foobar\\n\")")); } @Test public void failure_messages_highlight_the_problematic_calls() { listener.onFirst(); listener.onParameter(1); spy.replay(); listener.onFirst(); listener.onParameter(2); String message = fails(); assertThat(message, containsString("2. onParameter(1)\n" + SpyListener.ERROR_MARKER)); assertThat(message, containsString("2. onParameter(2)\n" + SpyListener.ERROR_MARKER)); assertThat(occurrencesOf(SpyListener.ERROR_MARKER, message), is(2)); } @Test(expected = IllegalStateException.class) public void cannot_call_replay_twice() { spy.replay(); spy.replay(); } @Test(expected = IllegalStateException.class) public void cannot_call_verify_without_first_calling_replay() { spy.verify(); } // helper methods private void passes() { spy.verify(); } private String fails() { try { spy.verify(); } catch (AssertionError expected) { return expected.getMessage(); } throw new AssertionError("expected to throw an AssertionError, but it did not"); } private static int occurrencesOf(String needle, String haystack) { int count = 0; for (int pos = 0; pos < haystack.length(); pos++) { pos = haystack.indexOf(needle, pos); if (pos < 0) { break; } count++; pos = pos + needle.length(); } return count; } private interface DummyListener { void onFirst(); void onSecond(); void onParameter(int parameter); void onStringParameter(String parameter); void onException(Throwable cause); void overloadedMethod(int one); void overloadedMethod(int one, int two); } }