/** * Copyright 2012-2020 The Feign Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package feign.mock; import static feign.Util.toByteArray; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Type; import java.util.List; import javax.net.ssl.HttpsURLConnection; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; import feign.Body; import feign.Feign; import feign.FeignException; import feign.Param; import feign.Request; import feign.RequestLine; import feign.Response; import feign.codec.DecodeException; import feign.codec.Decoder; import feign.gson.GsonDecoder; public class MockClientTest { interface GitHub { @RequestLine("GET /repos/{owner}/{repo}/contributors") List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo); @RequestLine("GET /repos/{owner}/{repo}/contributors?client_id={client_id}") List<Contributor> contributors(@Param("client_id") String clientId, @Param("owner") String owner, @Param("repo") String repo); @RequestLine("PATCH /repos/{owner}/{repo}/contributors") List<Contributor> patchContributors(@Param("owner") String owner, @Param("repo") String repo); @RequestLine("POST /repos/{owner}/{repo}/contributors") @Body("%7B\"login\":\"{login}\",\"type\":\"{type}\"%7D") Contributor create(@Param("owner") String owner, @Param("repo") String repo, @Param("login") String login, @Param("type") String type); } static class Contributor { String login; int contributions; } class AssertionDecoder implements Decoder { private final Decoder delegate; public AssertionDecoder(Decoder delegate) { this.delegate = delegate; } @Override public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException { assertThat(response.request(), notNullValue()); return delegate.decode(response, type); } } private GitHub github; private MockClient mockClient; @Before public void setup() throws IOException { try (InputStream input = getClass().getResourceAsStream("/fixtures/contributors.json")) { byte[] data = toByteArray(input); mockClient = new MockClient(); github = Feign.builder().decoder(new AssertionDecoder(new GsonDecoder())) .client(mockClient.ok(HttpMethod.GET, "/repos/netflix/feign/contributors", data) .ok(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=55") .ok(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=7 7", new ByteArrayInputStream(data)) .ok(HttpMethod.POST, "/repos/netflix/feign/contributors", "{\"login\":\"velo\",\"contributions\":0}") .noContent(HttpMethod.PATCH, "/repos/velo/feign-mock/contributors") .add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=1234567890", HttpsURLConnection.HTTP_NOT_FOUND) .add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=123456789", HttpsURLConnection.HTTP_INTERNAL_ERROR, new ByteArrayInputStream(data)) .add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=123456789", HttpsURLConnection.HTTP_INTERNAL_ERROR, "") .add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=123456789", HttpsURLConnection.HTTP_INTERNAL_ERROR, data)) .target(new MockTarget<>(GitHub.class)); } } @Test public void hitMock() { List<Contributor> contributors = github.contributors("netflix", "feign"); assertThat(contributors, hasSize(30)); mockClient.verifyStatus(); } @Test public void missMock() { try { github.contributors("velo", "feign-mock"); fail(); } catch (FeignException e) { assertThat(e.getMessage(), Matchers.containsString("404")); } } @Test public void missHttpMethod() { try { github.patchContributors("netflix", "feign"); fail(); } catch (FeignException e) { assertThat(e.getMessage(), Matchers.containsString("404")); } } @Test public void paramsEncoding() { List<Contributor> contributors = github.contributors("7 7", "netflix", "feign"); assertThat(contributors, hasSize(30)); mockClient.verifyStatus(); } @Test public void verifyInvocation() { Contributor contribution = github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); // making sure it received a proper response assertThat(contribution, notNullValue()); assertThat(contribution.login, equalTo("velo")); assertThat(contribution.contributions, equalTo(0)); List<Request> results = mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 1); assertThat(results, hasSize(1)); byte[] body = mockClient.verifyOne(HttpMethod.POST, "/repos/netflix/feign/contributors") .body(); assertThat(body, notNullValue()); String message = new String(body); assertThat(message, containsString("velo_at_github")); assertThat(message, containsString("preposterous hacker")); mockClient.verifyStatus(); } @Test public void verifyNone() { github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 1); try { mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 0); fail(); } catch (VerificationAssertionError e) { assertThat(e.getMessage(), containsString("Do not wanted")); assertThat(e.getMessage(), containsString("POST")); assertThat(e.getMessage(), containsString("/repos/netflix/feign/contributors")); } try { mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 3); fail(); } catch (VerificationAssertionError e) { assertThat(e.getMessage(), containsString("Wanted")); assertThat(e.getMessage(), containsString("POST")); assertThat(e.getMessage(), containsString("/repos/netflix/feign/contributors")); assertThat(e.getMessage(), containsString("'3'")); assertThat(e.getMessage(), containsString("'1'")); } } @Test public void verifyNotInvoked() { mockClient.verifyNever(HttpMethod.POST, "/repos/netflix/feign/contributors"); List<Request> results = mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 0); assertThat(results, hasSize(0)); try { mockClient.verifyOne(HttpMethod.POST, "/repos/netflix/feign/contributors"); fail(); } catch (VerificationAssertionError e) { assertThat(e.getMessage(), containsString("Wanted")); assertThat(e.getMessage(), containsString("POST")); assertThat(e.getMessage(), containsString("/repos/netflix/feign/contributors")); assertThat(e.getMessage(), containsString("never invoked")); } } @Test public void verifyNegative() { try { mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", -1); fail(); } catch (IllegalArgumentException e) { assertThat(e.getMessage(), containsString("non negative")); } } @Test public void verifyMultipleRequests() { mockClient.verifyNever(HttpMethod.POST, "/repos/netflix/feign/contributors"); github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); Request result = mockClient.verifyOne(HttpMethod.POST, "/repos/netflix/feign/contributors"); assertThat(result, notNullValue()); github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); List<Request> results = mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 2); assertThat(results, hasSize(2)); github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); results = mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 3); assertThat(results, hasSize(3)); mockClient.verifyStatus(); } @Test public void resetRequests() { mockClient.verifyNever(HttpMethod.POST, "/repos/netflix/feign/contributors"); github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); Request result = mockClient.verifyOne(HttpMethod.POST, "/repos/netflix/feign/contributors"); assertThat(result, notNullValue()); mockClient.resetRequests(); mockClient.verifyNever(HttpMethod.POST, "/repos/netflix/feign/contributors"); } }