/* * Copyright (c) 2012 - 2016 Jadler contributors * This program is made available under the terms of the MIT License. */ package net.jadler; import java.io.Writer; import static java.lang.String.format; import java.net.URI; import net.jadler.stubbing.server.StubHttpServerManager; import java.nio.charset.Charset; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import net.jadler.stubbing.Stubbing; import net.jadler.stubbing.HttpStub; import net.jadler.stubbing.StubResponse; import net.jadler.stubbing.StubbingFactory; import net.jadler.exception.JadlerException; import net.jadler.mocking.VerificationException; import net.jadler.mocking.Verifying; import net.jadler.stubbing.server.StubHttpServer; import org.apache.commons.collections.MultiMap; import org.apache.commons.collections.map.MultiValueMap; import org.apache.commons.io.output.StringBuilderWriter; import org.apache.log4j.Logger; import org.apache.log4j.PatternLayout; import org.apache.log4j.WriterAppender; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.times; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyObject; import static org.mockito.Mockito.doThrow; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.doAnswer; import static org.hamcrest.Matchers.is; public class JadlerMockerTest { private static final int DEFAULT_STATUS = 204; private static final String HEADER_NAME1 = "h1"; private static final String HEADER_VALUE1 = "v1"; private static final String HEADER_NAME2 = "h2"; private static final String HEADER_VALUE2 = "v2"; private static final Charset DEFAULT_ENCODING = Charset.forName("UTF-16"); private static final int PORT = 12345; private static final String HTTP_STUB1_TO_STRING = "http stub 1 toString"; private static final String HTTP_STUB2_TO_STRING = "http stub 2 toString"; private static final String HTTP_STUB1_MISMATCH = "http stub 1 mismatch"; private static final String HTTP_STUB2_MISMATCH = "http stub 2 mismatch"; private static final String MATCHER1_DESCRIPTION = "M1 description"; private static final String MATCHER1_MISMATCH = "M1 mismatch"; private static final String MATCHER2_DESCRIPTION = "M2 description"; private static final String MATCHER2_MISMATCH = "M2 mismatch"; private static final String COUNT_MATCHER_DESCRIPTION = "cnt matcher description"; private static final String COUNT_MATCHER_MISMATCH = "cnt matcher mismatch"; private static final Request[] REQUESTS = new Request[] { Request.builder().method("GET").requestURI(URI.create("/r1")).build(), Request.builder().method("GET").requestURI(URI.create("/r2")).build(), Request.builder().method("GET").requestURI(URI.create("/r3")).build(), Request.builder().method("GET").requestURI(URI.create("/r4")).build(), Request.builder().method("GET").requestURI(URI.create("/r5")).build()}; @Test(expected=IllegalArgumentException.class) public void constructor1() { new JadlerMocker(null); fail("server cannot be null"); } @Test public void constructor2() { new JadlerMocker(mock(StubHttpServer.class)); } @Test(expected=IllegalArgumentException.class) public void constructor3() { new JadlerMocker(null, null); fail("neither server nor stubbing factory can be null"); } @Test(expected=IllegalArgumentException.class) public void constructor4() { new JadlerMocker(null, mock(StubbingFactory.class)); fail("server cannot be null"); } @Test(expected=IllegalArgumentException.class) public void constructor5() { new JadlerMocker(mock(StubHttpServer.class), null); fail("stubbing factory cannot be null"); } @Test public void constructor6() { new JadlerMocker(mock(StubHttpServer.class), mock(StubbingFactory.class)); } @Test(expected=IllegalStateException.class) public void startNotStopped() { final JadlerMocker mocker = new JadlerMocker(mock(StubHttpServer.class)); mocker.start(); mocker.start(); fail("mocker has been started already, cannot be started again without being stopped first"); } @Test(expected=JadlerException.class) public void startException() throws Exception { final StubHttpServer server = mock(StubHttpServer.class); doThrow(new Exception()).when(server).start(); new JadlerMocker(server).start(); fail("server threw an exception"); } @Test public void start() throws Exception { final StubHttpServer server = mock(StubHttpServer.class); final JadlerMocker jadlerMocker = new JadlerMocker(server); jadlerMocker.start(); verify(server, times(1)).start(); verify(server, times(1)).registerRequestManager(eq(jadlerMocker)); verifyNoMoreInteractions(server); } @Test(expected=IllegalStateException.class) public void closeNotStarted() { new JadlerMocker(mock(StubHttpServer.class)).close(); fail("mocker cannot be stopped without being started"); } @Test(expected=JadlerException.class) public void closeException() throws Exception { final StubHttpServer server = mock(StubHttpServer.class); doThrow(new Exception()).when(server).start(); final StubHttpServerManager mocker = new JadlerMocker(server); mocker.start(); mocker.close(); fail("server threw an exception"); } @Test public void close() throws Exception { final StubHttpServer server = mock(StubHttpServer.class); final JadlerMocker mocker = new JadlerMocker(server); mocker.start(); mocker.close(); verify(server, times(1)).stop(); } @Test public void isStarted() throws Exception { final StubHttpServer server = mock(StubHttpServer.class); final JadlerMocker jadlerMocker = new JadlerMocker(server); assertThat(jadlerMocker.isStarted(), is(false)); jadlerMocker.start(); assertThat(jadlerMocker.isStarted(), is(true)); jadlerMocker.close(); assertThat(jadlerMocker.isStarted(), is(false)); } @Test(expected=IllegalStateException.class) public void getStubHttpServerPortNotStarted() { final StubHttpServerManager serverManager = new JadlerMocker(mock(StubHttpServer.class)); serverManager.getStubHttpServerPort(); fail("server has not been started yet, cannot retrieve the port number"); } @Test public void getStubHttpServerPort() { final StubHttpServer server = mock(StubHttpServer.class); when(server.getPort()).thenReturn(PORT); final StubHttpServerManager serverManager = new JadlerMocker(server); serverManager.start(); assertThat(serverManager.getStubHttpServerPort(), is(PORT)); } @Test(expected=IllegalArgumentException.class) public void addDefaultHeaderWrongParam1() { new JadlerMocker(mock(StubHttpServer.class)).addDefaultHeader(null, "abcd"); fail("default header name cannot be null"); } @Test(expected=IllegalArgumentException.class) public void addDefaultHeaderWrongParam2() { new JadlerMocker(mock(StubHttpServer.class)).addDefaultHeader("abcd", null); fail("default header value cannot be null"); } @Test(expected=IllegalStateException.class) public void addDefaultHeaderWrongState() { final JadlerMocker mocker = new JadlerMocker(mock(StubHttpServer.class)); //calling provideStubResponseFor finishes the configuration phase, a default header cannot be added anymore mocker.provideStubResponseFor(prepareEmptyMockRequest()); mocker.addDefaultHeader("abcd", "efgh"); fail("default header cannot be added anymore"); } @Test public void addDefaultHeader1() { //empty header value is valid new JadlerMocker(mock(StubHttpServer.class)).addDefaultHeader("abcd", ""); } @Test public void addDefaultHeader2() { new JadlerMocker(mock(StubHttpServer.class)).addDefaultHeader("abcd", "efgh"); } @Test(expected=IllegalArgumentException.class) public void setDefaultStatusWrongParam() { new JadlerMocker(mock(StubHttpServer.class)).setDefaultStatus(-1); fail("defaultStatus must be at least 0"); } @Test(expected=IllegalStateException.class) public void setDefaultStatusWrongState() { final JadlerMocker mocker = new JadlerMocker(mock(StubHttpServer.class)); mocker.provideStubResponseFor(prepareEmptyMockRequest()); mocker.setDefaultStatus(200); fail("default status cannot be set anymore"); } @Test public void setDefaultStatus() { final JadlerMocker mocker = new JadlerMocker(mock(StubHttpServer.class)); mocker.setDefaultStatus(200); } @Test(expected=IllegalArgumentException.class) public void setDefaultEncodingWrongParam() { new JadlerMocker(mock(StubHttpServer.class)).setDefaultEncoding(null); fail("defaultEncoding mustn't be null"); } @Test(expected=IllegalStateException.class) public void setDefaultEncodingWrongState() { final JadlerMocker mocker = new JadlerMocker(mock(StubHttpServer.class)); mocker.provideStubResponseFor(prepareEmptyMockRequest()); mocker.setDefaultEncoding(DEFAULT_ENCODING); fail("default encoding cannot be set anymore"); } @Test public void setDefaultEncoding() { final JadlerMocker mocker = new JadlerMocker(mock(StubHttpServer.class)); mocker.setDefaultEncoding(DEFAULT_ENCODING); } @Test(expected=IllegalStateException.class) public void onRequestWrongState() { final JadlerMocker mocker = new JadlerMocker(mock(StubHttpServer.class)); mocker.provideStubResponseFor(prepareEmptyMockRequest()); mocker.onRequest(); fail("The mocker has already provided first response, cannot do any stubbing anymore"); } @Test public void onRequest() { final Stubbing stubbing = mock(Stubbing.class); final StubbingFactory sf = mock(StubbingFactory.class); when(sf.createStubbing(any(Charset.class), anyInt(), any(MultiMap.class))).thenReturn(stubbing); final StubHttpServer server = mock(StubHttpServer.class); final JadlerMocker mocker = new JadlerMocker(server, sf); final Stubbing result = (Stubbing) mocker.onRequest(); assertThat(result, is(stubbing)); } /* * Tests that defaults (status, headers, encoding) are used correctly when creating a stubbing instance. */ @Test public void onRequestWithDefaults() { final StubHttpServer server = mock(StubHttpServer.class); final StubbingFactory sf = mock(StubbingFactory.class); final JadlerMocker mocker = new JadlerMocker(server, sf); mocker.setDefaultStatus(DEFAULT_STATUS); mocker.addDefaultHeader(HEADER_NAME1, HEADER_VALUE1); mocker.addDefaultHeader(HEADER_NAME2, HEADER_VALUE2); mocker.setDefaultEncoding(DEFAULT_ENCODING); mocker.onRequest(); //verify the Stubbing instance was created with the given defaults final MultiMap defaultHeaders = new MultiValueMap(); defaultHeaders.put(HEADER_NAME1, HEADER_VALUE1); defaultHeaders.put(HEADER_NAME2, HEADER_VALUE2); verify(sf, times(1)).createStubbing(eq(DEFAULT_ENCODING), eq(DEFAULT_STATUS), eq(defaultHeaders)); } /* * Tests that if no default status is set, 200 is used as a super-default */ @Test public void onRequestNoDefaultStatus() { final StubHttpServer server = mock(StubHttpServer.class); final StubbingFactory sf = mock(StubbingFactory.class); final JadlerMocker mocker = new JadlerMocker(server, sf); mocker.onRequest(); //verify the Stubbing instance was created with the default 200 response status verify(sf, times(1)).createStubbing(any(Charset.class), eq(200), any(MultiMap.class)); } /* * */ @Test public void onRequestNoDefaultEncoding() { final StubHttpServer server = mock(StubHttpServer.class); final StubbingFactory sf = mock(StubbingFactory.class); final JadlerMocker mocker = new JadlerMocker(server, sf); mocker.onRequest(); //verify the Stubbing instance was created with the default UTF-8 response encoding verify(sf, times(1)).createStubbing(eq(Charset.forName("UTF-8")), anyInt(), any(MultiMap.class)); } @Test public void onRequestNoDefaultHeaders() { final StubHttpServer server = mock(StubHttpServer.class); final StubbingFactory sf = mock(StubbingFactory.class); final JadlerMocker mocker = new JadlerMocker(server, sf); mocker.onRequest(); //verify the Stubbing instance was created with no default headers verify(sf, times(1)).createStubbing(any(Charset.class), anyInt(), eq(new MultiValueMap())); } //following provideResponseFor() tests are far from being just standard unit tests since they //need a cooperation of two or more JadlerMocker methods. @Test public void provideStubResponseFor() { final Request req = prepareEmptyMockRequest(); //rule1 matches the given request (param of the provideResponseFor method) so it must be returned from //the tested method final HttpStub rule1 = mock(HttpStub.class); final Stubbing stubbing1 = mock(Stubbing.class); when(stubbing1.createRule()).thenReturn(rule1); when(rule1.matches(eq(req))).thenReturn(true); final StubResponse resp1 = StubResponse.EMPTY; when(rule1.nextResponse(eq(req))).thenReturn(resp1); //rule2 doesn't match the given request final HttpStub rule2 = mock(HttpStub.class); final Stubbing stubbing2 = mock(Stubbing.class); when(stubbing2.createRule()).thenReturn(rule2); when(rule2.matches(eq(req))).thenReturn(false); final StubbingFactory sf = mock(StubbingFactory.class); when(sf.createStubbing(any(Charset.class), anyInt(), any(MultiMap.class))).thenReturn(stubbing1, stubbing2); final StubHttpServer server = mock(StubHttpServer.class); final JadlerMocker mocker = new JadlerMocker(server, sf); //calling onRequest twice so stubbing1 and stubbing2 are created in the JadlerMocker instance mocker.onRequest(); mocker.onRequest(); assertThat(mocker.provideStubResponseFor(req), is(resp1)); } @Test public void provideStubResponseFor2() { final Request req = prepareEmptyMockRequest(); //neither rule1 nor rule2 matches, default not-found response must be returned final HttpStub rule1 = mock(HttpStub.class); final Stubbing stubbing1 = mock(Stubbing.class); when(stubbing1.createRule()).thenReturn(rule1); when(rule1.matches(eq(req))).thenReturn(false); when(rule1.toString()).thenReturn(HTTP_STUB1_TO_STRING); when(rule1.describeMismatch(eq(req))).thenReturn(HTTP_STUB1_MISMATCH); final HttpStub rule2 = mock(HttpStub.class); final Stubbing stubbing2 = mock(Stubbing.class); when(stubbing2.createRule()).thenReturn(rule2); when(rule2.matches(eq(req))).thenReturn(false); when(rule2.toString()).thenReturn(HTTP_STUB2_TO_STRING); when(rule2.describeMismatch(eq(req))).thenReturn(HTTP_STUB2_MISMATCH); final StubbingFactory sf = mock(StubbingFactory.class); when(sf.createStubbing(any(Charset.class), anyInt(), any(MultiMap.class))) .thenReturn(stubbing1, stubbing2); final StubHttpServer server = mock(StubHttpServer.class); final JadlerMocker mocker = new JadlerMocker(server, sf); //calling onRequest twice so stubbing1 and stubbing2 are created in the JadlerMocker instance mocker.onRequest(); mocker.onRequest(); final Writer w = this.createAppenderWriter(); try { final StubResponse res = mocker.provideStubResponseFor(req); assertThat(res, is(not(nullValue()))); assertThat(res.getStatus(), is(404)); assertThat(res.getDelay(), is(0L)); assertThat(res.getBody(), is("No stub response found for the incoming request".getBytes())); assertThat(res.getEncoding(), is(Charset.forName("UTF-8"))); final KeyValues expectedHeaders = new KeyValues().add("Content-Type", "text/plain; charset=utf-8"); assertThat(res.getHeaders(), is(expectedHeaders)); } finally { this.clearLog4jSetup(); } assertThat(w.toString(), is(format("[INFO] No suitable rule found. Reason:\n" + "The rule '%s' cannot be applied. Mismatch:\n%s\n" + "The rule '%s' cannot be applied. Mismatch:\n%s\n", HTTP_STUB1_TO_STRING, HTTP_STUB1_MISMATCH, HTTP_STUB2_TO_STRING, HTTP_STUB2_MISMATCH))); } @Test public void provideStubResponseFor3() { final Request req = prepareEmptyMockRequest(); //both rules matches the request, the latter must be provided final HttpStub rule1 = mock(HttpStub.class); final Stubbing stubbing1 = mock(Stubbing.class); when(stubbing1.createRule()).thenReturn(rule1); when(rule1.matches(eq(req))).thenReturn(true); final StubResponse resp1 = StubResponse.EMPTY; when(rule1.nextResponse(eq(req))).thenReturn(resp1); final HttpStub rule2 = mock(HttpStub.class); final Stubbing stubbing2 = mock(Stubbing.class); when(stubbing2.createRule()).thenReturn(rule2); when(rule2.matches(eq(req))).thenReturn(true); final StubResponse resp2 = StubResponse.EMPTY; when(rule2.nextResponse(eq(req))).thenReturn(resp2); final StubbingFactory sf = mock(StubbingFactory.class); when(sf.createStubbing(any(Charset.class), anyInt(), any(MultiMap.class))) .thenReturn(stubbing1, stubbing2); final StubHttpServer server = mock(StubHttpServer.class); final JadlerMocker mocker = new JadlerMocker(server, sf); //calling onRequest twice so stubbing1 and stubbing2 are created in the JadlerMocker instance mocker.onRequest(); mocker.onRequest(); assertThat(mocker.provideStubResponseFor(req), is(resp2)); } @Test public void reset() { final Request req = prepareEmptyMockRequest(); final HttpStub rule1 = mock(HttpStub.class); final Stubbing stubbing1 = mock(Stubbing.class); when(stubbing1.createRule()).thenReturn(rule1); when(rule1.matches(eq(req))).thenReturn(true); final StubResponse resp1 = StubResponse.builder().build(); when(rule1.nextResponse(eq(req))).thenReturn(resp1); final HttpStub rule2 = mock(HttpStub.class); final Stubbing stubbing2 = mock(Stubbing.class); when(stubbing2.createRule()).thenReturn(rule2); when(rule2.matches(eq(req))).thenReturn(true); final StubResponse resp2 = StubResponse.builder().build(); when(rule2.nextResponse(eq(req))).thenReturn(resp2); final StubbingFactory sf = mock(StubbingFactory.class); when(sf.createStubbing(any(Charset.class), anyInt(), any(MultiMap.class))).thenReturn(stubbing1, stubbing2); final StubHttpServer server = mock(StubHttpServer.class); final JadlerMocker mocker = new JadlerMocker(server, sf); //calling onRequest so stubbing1 is created in the JadlerMocker instance mocker.onRequest(); assertThat(mocker.provideStubResponseFor(req), is(resp1)); mocker.reset(); //calling onRequest stubbing2 is created in the JadlerMocker instance mocker.onRequest(); assertThat(mocker.provideStubResponseFor(req), is(resp2)); } @Test(expected = IllegalStateException.class) public void verifyThatRequest_noRequestRecording() { final JadlerMocker mocker = new JadlerMocker(mock(StubHttpServer.class)); mocker.setRecordRequests(false); mocker.verifyThatRequest(); } @Test public void verifyThatRequest() { final Verifying ongoingVerifying = new JadlerMocker(mock(StubHttpServer.class)).verifyThatRequest(); assertThat(ongoingVerifying, is(not(nullValue()))); } @Test(expected = IllegalArgumentException.class) @SuppressWarnings("unchecked") public void evaluateVerification_illegalArgument1() { new JadlerMocker(mock(StubHttpServer.class)).evaluateVerification(null, mock(Matcher.class)); } @Test(expected = IllegalArgumentException.class) @SuppressWarnings("unchecked") public void evaluateVerification_illegalArgument2() { new JadlerMocker(mock(StubHttpServer.class)).evaluateVerification( Collections.<Matcher<? super Request>>singleton(mock(Matcher.class)), null); } @Test(expected = IllegalStateException.class) @SuppressWarnings("unchecked") public void evaluateVerification_recordingDisabled() { final JadlerMocker mocker = new JadlerMocker(mock(StubHttpServer.class)); mocker.setRecordRequests(false); mocker.evaluateVerification(Collections.<Matcher<? super Request>>singleton(mock(Matcher.class)), mock(Matcher.class)); } @Test public void evaluateVerification_2_matching_positive() { final JadlerMocker mocker = this.createMockerWithRequests(); final Matcher<Request> m1 = this.createRequestMatcher(MATCHER1_DESCRIPTION, MATCHER1_MISMATCH); final Matcher<Request> m2 = this.createRequestMatcher(MATCHER2_DESCRIPTION, MATCHER2_MISMATCH); //R0 is not matched when(m1.matches(eq(REQUESTS[0]))).thenReturn(false); when(m2.matches(eq(REQUESTS[0]))).thenReturn(false); //R1 is matched when(m1.matches(eq(REQUESTS[1]))).thenReturn(true); when(m2.matches(eq(REQUESTS[1]))).thenReturn(true); //R2 is not matched when(m1.matches(eq(REQUESTS[2]))).thenReturn(false); when(m2.matches(eq(REQUESTS[2]))).thenReturn(true); //R3 is not matched when(m1.matches(eq(REQUESTS[3]))).thenReturn(true); when(m2.matches(eq(REQUESTS[3]))).thenReturn(false); //R4 is matched when(m1.matches(eq(REQUESTS[4]))).thenReturn(true); when(m2.matches(eq(REQUESTS[4]))).thenReturn(true); //2 requests matching, 2 expected final Matcher<Integer> countMatcher = this.createCountMatcherFor(2, 2, COUNT_MATCHER_DESCRIPTION, COUNT_MATCHER_MISMATCH); mocker.evaluateVerification(collectionOf(m1, m2), countMatcher); } @Test public void evaluateVerification_0_matching_positive() { final JadlerMocker mocker = this.createMockerWithRequests(); final Matcher<Request> m1 = this.createRequestMatcher(MATCHER1_DESCRIPTION, MATCHER1_MISMATCH); final Matcher<Request> m2 = this.createRequestMatcher(MATCHER2_DESCRIPTION, MATCHER2_MISMATCH); //R0 is not matched when(m1.matches(eq(REQUESTS[0]))).thenReturn(false); when(m2.matches(eq(REQUESTS[0]))).thenReturn(false); //R1 is not matched when(m1.matches(eq(REQUESTS[1]))).thenReturn(true); when(m2.matches(eq(REQUESTS[1]))).thenReturn(false); //R2 is not matched when(m1.matches(eq(REQUESTS[2]))).thenReturn(false); when(m2.matches(eq(REQUESTS[2]))).thenReturn(true); //R3 is not matched when(m1.matches(eq(REQUESTS[3]))).thenReturn(true); when(m2.matches(eq(REQUESTS[3]))).thenReturn(false); //R4 is not matched when(m1.matches(eq(REQUESTS[4]))).thenReturn(false); when(m2.matches(eq(REQUESTS[4]))).thenReturn(true); //0 requests matching, 0 expected final Matcher<Integer> countMatcher = this.createCountMatcherFor(0, 0, COUNT_MATCHER_DESCRIPTION, COUNT_MATCHER_MISMATCH); mocker.evaluateVerification(collectionOf(m1, m2), countMatcher); } @Test public void evaluateVerification_0_predicates_5_matching_positive() { final JadlerMocker mocker = this.createMockerWithRequests(); //5 requests matching (=received in this case), 5 expected final int actualCount = 5; final Matcher<Integer> countMatcher = this.createCountMatcherFor(5, actualCount, COUNT_MATCHER_DESCRIPTION, COUNT_MATCHER_MISMATCH); mocker.evaluateVerification(Collections.<Matcher<? super Request>>emptySet(), countMatcher); } @Test public void evaluateVerification_0_requests_0_matching_positive() { final JadlerMocker mocker = new JadlerMocker(mock(StubHttpServer.class)); final Matcher<Request> m1 = this.createRequestMatcher(MATCHER1_DESCRIPTION, MATCHER1_MISMATCH); final Matcher<Request> m2 = this.createRequestMatcher(MATCHER2_DESCRIPTION, MATCHER2_MISMATCH); //0 requests matching (=received in this case), 0 expected final Matcher<Integer> countMatcher = this.createCountMatcherFor(0, 0, COUNT_MATCHER_DESCRIPTION, COUNT_MATCHER_MISMATCH); mocker.evaluateVerification(collectionOf(m1, m2), countMatcher); } @Test public void evaluateVerification_2_matching_negative() { final JadlerMocker mocker = this.createMockerWithRequests(); final Matcher<Request> m1 = this.createRequestMatcher(MATCHER1_DESCRIPTION, MATCHER1_MISMATCH); final Matcher<Request> m2 = this.createRequestMatcher(MATCHER2_DESCRIPTION, MATCHER2_MISMATCH); //R0 is not matched when(m1.matches(eq(REQUESTS[0]))).thenReturn(false); when(m2.matches(eq(REQUESTS[0]))).thenReturn(false); //R1 is matched when(m1.matches(eq(REQUESTS[1]))).thenReturn(true); when(m2.matches(eq(REQUESTS[1]))).thenReturn(true); //R2 is not matched when(m1.matches(eq(REQUESTS[2]))).thenReturn(false); when(m2.matches(eq(REQUESTS[2]))).thenReturn(true); //R3 is not matched when(m1.matches(eq(REQUESTS[3]))).thenReturn(true); when(m2.matches(eq(REQUESTS[3]))).thenReturn(false); //R4 is matched when(m1.matches(eq(REQUESTS[4]))).thenReturn(true); when(m2.matches(eq(REQUESTS[4]))).thenReturn(true); //2 requests matching, 1 expected final Matcher<Integer> countMatcher = this.createCountMatcherFor(1, 2, COUNT_MATCHER_DESCRIPTION, COUNT_MATCHER_MISMATCH); final Writer w = this.createAppenderWriter(); try { mocker.evaluateVerification(collectionOf(m1, m2), countMatcher); fail("VerificationException is supposed to be thrown here"); } catch (final VerificationException e) { assertThat(e.getMessage(), is(expectedMessage())); assertThat(w.toString(), is(format( "[INFO] Verification failed, here is a list of requests received so far:\n" + "Request #1: %s\n" + " matching predicates: <none>\n" + " clashing predicates:\n" + " %s\n" + " %s\n" + "Request #2: %s\n" + " matching predicates:\n" + " %s\n" + " %s\n" + " clashing predicates: <none>\n" + "Request #3: %s\n" + " matching predicates:\n" + " %s\n" + " clashing predicates:\n" + " %s\n" + "Request #4: %s\n" + " matching predicates:\n" + " %s\n" + " clashing predicates:\n" + " %s\n" + "Request #5: %s\n" + " matching predicates:\n" + " %s\n" + " %s\n" + " clashing predicates: <none>", REQUESTS[0], MATCHER1_MISMATCH, MATCHER2_MISMATCH, REQUESTS[1], MATCHER1_DESCRIPTION, MATCHER2_DESCRIPTION, REQUESTS[2], MATCHER2_DESCRIPTION, MATCHER1_MISMATCH, REQUESTS[3], MATCHER1_DESCRIPTION, MATCHER2_MISMATCH, REQUESTS[4], MATCHER1_DESCRIPTION, MATCHER2_DESCRIPTION))); } finally { this.clearLog4jSetup(); } } @Test public void evaluateVerification_0_matching_negative() { final JadlerMocker mocker = this.createMockerWithRequests(); final Matcher<Request> m1 = this.createRequestMatcher(MATCHER1_DESCRIPTION, MATCHER1_MISMATCH); final Matcher<Request> m2 = this.createRequestMatcher(MATCHER2_DESCRIPTION, MATCHER2_MISMATCH); //R0 is not matched when(m1.matches(eq(REQUESTS[0]))).thenReturn(false); when(m2.matches(eq(REQUESTS[0]))).thenReturn(false); //R1 is not matched when(m1.matches(eq(REQUESTS[1]))).thenReturn(false); when(m2.matches(eq(REQUESTS[1]))).thenReturn(true); //R2 is not matched when(m1.matches(eq(REQUESTS[2]))).thenReturn(false); when(m2.matches(eq(REQUESTS[2]))).thenReturn(true); //R3 is not matched when(m1.matches(eq(REQUESTS[3]))).thenReturn(true); when(m2.matches(eq(REQUESTS[3]))).thenReturn(false); //R4 is not matched when(m1.matches(eq(REQUESTS[4]))).thenReturn(true); when(m2.matches(eq(REQUESTS[4]))).thenReturn(false); //0 requests matching, 1 expected final Matcher<Integer> countMatcher = this.createCountMatcherFor(1, 0, COUNT_MATCHER_DESCRIPTION, COUNT_MATCHER_MISMATCH); final Writer w = this.createAppenderWriter(); try { mocker.evaluateVerification(collectionOf(m1, m2), countMatcher); fail("VerificationException is supposed to be thrown here"); } catch (final VerificationException e) { assertThat(e.getMessage(), is(expectedMessage())); assertThat(w.toString(), is(format( "[INFO] Verification failed, here is a list of requests received so far:\n" + "Request #1: %s\n" + " matching predicates: <none>\n" + " clashing predicates:\n" + " %s\n" + " %s\n" + "Request #2: %s\n" + " matching predicates:\n" + " %s\n" + " clashing predicates:\n" + " %s\n" + "Request #3: %s\n" + " matching predicates:\n" + " %s\n" + " clashing predicates:\n" + " %s\n" + "Request #4: %s\n" + " matching predicates:\n" + " %s\n" + " clashing predicates:\n" + " %s\n" + "Request #5: %s\n" + " matching predicates:\n" + " %s\n" + " clashing predicates:\n" + " %s", REQUESTS[0], MATCHER1_MISMATCH, MATCHER2_MISMATCH, REQUESTS[1], MATCHER2_DESCRIPTION, MATCHER1_MISMATCH, REQUESTS[2], MATCHER2_DESCRIPTION, MATCHER1_MISMATCH, REQUESTS[3], MATCHER1_DESCRIPTION, MATCHER2_MISMATCH, REQUESTS[4], MATCHER1_DESCRIPTION, MATCHER2_MISMATCH))); } finally { this.clearLog4jSetup(); } } @Test public void evaluateVerification_0_predicates_5_matching_negative() { final JadlerMocker mocker = this.createMockerWithRequests(); //5 requests matching (=received in this case), 4 expected final Matcher<Integer> countMatcher = this.createCountMatcherFor(4, 5, COUNT_MATCHER_DESCRIPTION, COUNT_MATCHER_MISMATCH); final Writer w = this.createAppenderWriter(); try { mocker.evaluateVerification(Collections.<Matcher<? super Request>>emptySet(), countMatcher); fail("VerificationException is supposed to be thrown here"); } catch (final VerificationException e) { assertThat(e.getMessage(), is(format("The number of http requests was expected to be %s, but %s", COUNT_MATCHER_DESCRIPTION, COUNT_MATCHER_MISMATCH))); assertThat(w.toString(), is(format( "[INFO] Verification failed, here is a list of requests received so far:\n" + "Request #1: %s\n" + " matching predicates: <none>\n" + " clashing predicates: <none>\n" + "Request #2: %s\n" + " matching predicates: <none>\n" + " clashing predicates: <none>\n" + "Request #3: %s\n" + " matching predicates: <none>\n" + " clashing predicates: <none>\n" + "Request #4: %s\n" + " matching predicates: <none>\n" + " clashing predicates: <none>\n" + "Request #5: %s\n" + " matching predicates: <none>\n" + " clashing predicates: <none>", REQUESTS[0], REQUESTS[1], REQUESTS[2], REQUESTS[3], REQUESTS[4]))); } finally { this.clearLog4jSetup(); } } @Test public void evaluateVerification_0_requests_0_matching_negative() { final JadlerMocker mocker = new JadlerMocker(mock(StubHttpServer.class)); final Matcher<Request> m1 = this.createRequestMatcher(MATCHER1_DESCRIPTION, MATCHER1_MISMATCH); final Matcher<Request> m2 = this.createRequestMatcher(MATCHER2_DESCRIPTION, MATCHER2_MISMATCH); //0 requests matching (=received in this case), 0 expected final Matcher<Integer> countMatcher = this.createCountMatcherFor(1, 0, COUNT_MATCHER_DESCRIPTION, COUNT_MATCHER_MISMATCH); final Writer w = this.createAppenderWriter(); try { mocker.evaluateVerification(collectionOf(m1, m2), countMatcher); fail("VerificationException is supposed to be thrown here"); } catch (final VerificationException e) { assertThat(e.getMessage(), is(expectedMessage())); assertThat(w.toString(), is("[INFO] Verification failed, here is a list of requests received so far: <none>")); } finally { this.clearLog4jSetup(); } } @Test(expected=IllegalArgumentException.class) @SuppressWarnings("deprecation") public void numberOfRequestsMatchingInvalidArgument() { new JadlerMocker(mock(StubHttpServer.class)).numberOfRequestsMatching(null); fail("matchers cannot be null"); } @Test(expected=IllegalStateException.class) @SuppressWarnings({"unchecked", "deprecation"}) public void numberOfRequestsMatching_noRequestRecording() { final JadlerMocker mocker = new JadlerMocker(mock(StubHttpServer.class)); mocker.setRecordRequests(false); mocker.numberOfRequestsMatching(Collections.<Matcher<? super Request>>singletonList(mock(Matcher.class))); } @Test public void numberOfRequestsMatchingNoReceivedRequest() { @SuppressWarnings("unchecked") final Matcher<? super Request> m1 = mock(Matcher.class); when(m1.matches(anyObject())).thenReturn(true); final JadlerMocker mocker = new JadlerMocker(mock(StubHttpServer.class)); @SuppressWarnings("deprecation") final int cnt = mocker.numberOfRequestsMatching(Collections.<Matcher<? super Request>>singletonList(m1)); assertThat(cnt, is(0)); //no request received yet, must be zero } @Test public void numberOfRequestsMatchingNoPredicates() { final JadlerMocker mocker = new JadlerMocker(mock(StubHttpServer.class)); //calling provideStubResponseFor for all three requests so these get recorder in the mocker mocker.provideStubResponseFor(mock(Request.class)); mocker.provideStubResponseFor(mock(Request.class)); mocker.provideStubResponseFor(mock(Request.class)); @SuppressWarnings("deprecation") final int cnt = mocker.numberOfRequestsMatching(Collections.<Matcher<? super Request>>emptyList()); assertThat(cnt, is(3)); } @Test @SuppressWarnings("deprecation") public void numberOfRequestsMatching() { final Request req1 = mock(Request.class); final Request req2 = mock(Request.class); final Request req3 = mock(Request.class); @SuppressWarnings("unchecked") final Matcher<? super Request> m1 = mock(Matcher.class); when(m1.matches(req1)).thenReturn(true); when(m1.matches(req2)).thenReturn(false); when(m1.matches(req3)).thenReturn(true); final JadlerMocker mocker = new JadlerMocker(mock(StubHttpServer.class)); //calling provideStubResponseFor for all three requests so these get recorder in the mocker mocker.provideStubResponseFor(req1); mocker.provideStubResponseFor(req2); mocker.provideStubResponseFor(req3); final Collection<Matcher<? super Request>> singletonMatcher = Collections.<Matcher<? super Request>>singletonList(m1); assertThat(mocker.numberOfRequestsMatching(singletonMatcher), is(2)); mocker.reset(); assertThat(mocker.numberOfRequestsMatching(singletonMatcher), is(0)); } private Request prepareEmptyMockRequest() { return Request.builder() .method("GET") .requestURI(URI.create("http://localhost/")) .build(); } private Matcher<Request> createRequestMatcher(final String desc, final String mismatch) { @SuppressWarnings("unchecked") final Matcher<Request> m = mock(Matcher.class); this.addDescriptionTo(m, desc); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { final Description desc = (Description) invocation.getArguments()[1]; desc.appendText(mismatch); return null; } }).when(m).describeMismatch(any(Request.class), any(Description.class)); return m; } /* * Creates a Matcher<Integer> instance to be used as a matcher of a number of requests during a verification * @param expectedCount expected number of requests received so far which suit the verification description * @param actualCount actual number of requests received so far which suit the verification description * @param desc description of the matcher * @param mismatch description of a mismatch * @return a configured matcher */ private Matcher<Integer> createCountMatcherFor(final int expectedCount, final int actualCount, final String desc, final String mismatch) { @SuppressWarnings("unchecked") final Matcher<Integer> m = mock(Matcher.class); when(m.matches(eq(expectedCount))).thenReturn(true); doAnswer(new Answer<Void>() { @Override public Void answer(final InvocationOnMock invocation) throws Throwable { final Description desc = (Description) invocation.getArguments()[1]; desc.appendText(mismatch); return null; } }).when(m).describeMismatch(eq(actualCount), any(Description.class)); this.addDescriptionTo(m, desc); return m; } private JadlerMocker createMockerWithRequests() { final JadlerMocker mocker = new JadlerMocker(mock(StubHttpServer.class)); //register all requests for (final Request r: REQUESTS) { mocker.provideStubResponseFor(r); } return mocker; } private void addDescriptionTo(final Matcher<?> m, final String desc) { doAnswer(new Answer<Void>() { @Override public Void answer(final InvocationOnMock invocation) throws Throwable { final Description d = (Description) invocation.getArguments()[0]; d.appendText(desc); return null; } }).when(m).describeTo(any(Description.class)); } @SuppressWarnings("unchecked") private Collection<Matcher<? super Request>> collectionOf(Matcher<? super Request> m1, Matcher<? super Request> m2) { return Arrays.<Matcher<? super Request>>asList(m1, m2); } private String expectedMessage() { return format("The number of http requests having %s AND %s was expected to be %s, but %s", MATCHER1_DESCRIPTION, MATCHER2_DESCRIPTION, COUNT_MATCHER_DESCRIPTION, COUNT_MATCHER_MISMATCH); } private Writer createAppenderWriter() { final Writer w = new StringBuilderWriter(); final WriterAppender appender = new WriterAppender(); appender.setLayout(new PatternLayout("[%p] %m")); appender.setWriter(w); Logger.getRootLogger().addAppender(appender); return w; } private void clearLog4jSetup() { Logger.getRootLogger().getLoggerRepository().resetConfiguration(); } }