You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2021/06/19 12:32:02 UTC

[httpcomponents-client] branch HTTPCLIENT-1244 updated (d681e24 -> 51c03ed)

This is an automated email from the ASF dual-hosted git repository.

olegk pushed a change to branch HTTPCLIENT-1244
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git.


 discard d681e24  HTTPCLIENT-1244: Replaced EasyMock with Mockito in the HTTP cache unit tests
     new 51c03ed  HTTPCLIENT-1244: Replaced EasyMock with Mockito in the HTTP cache unit tests

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (d681e24)
            \
             N -- N -- N   refs/heads/HTTPCLIENT-1244 (51c03ed)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .travis.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

[httpcomponents-client] 01/01: HTTPCLIENT-1244: Replaced EasyMock with Mockito in the HTTP cache unit tests

Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

olegk pushed a commit to branch HTTPCLIENT-1244
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git

commit 51c03ed6f5654c4c327859736c88a41507281fbb
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Sat Jun 12 21:57:44 2021 +0200

    HTTPCLIENT-1244: Replaced EasyMock with Mockito in the HTTP cache unit tests
---
 .travis.yml                                        |    6 +-
 httpclient5-cache/pom.xml                          |    5 -
 .../http/impl/cache/AbstractProtocolTest.java      |  186 ---
 .../hc/client5/http/impl/cache/DummyBackend.java   |   65 -
 .../hc/client5/http/impl/cache/HttpTestUtils.java  |   14 +-
 .../client5/http/impl/cache/RequestEquivalent.java |   23 +-
 .../http/impl/cache/ResponseEquivalent.java        |   23 +-
 .../client5/http/impl/cache/TestCachingExec.java   |  458 ------
 .../http/impl/cache/TestCachingExecChain.java      |  911 +++--------
 .../impl/cache/TestProtocolAllowedBehavior.java    |   96 +-
 .../http/impl/cache/TestProtocolDeviations.java    |  180 +--
 .../impl/cache/TestProtocolRecommendations.java    |  427 +++---
 .../http/impl/cache/TestProtocolRequirements.java  | 1587 +++++++-------------
 .../http/impl/cache/TestRFC5861Compliance.java     |  228 +--
 pom.xml                                            |    7 -
 15 files changed, 1254 insertions(+), 2962 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 79392d4..1e0a683 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -27,14 +27,10 @@ addons:
       - maven
       
 jdk:
-  - oraclejdk8
   - openjdk12
-  - oraclejdk12
+  - oraclejdk16
   - openjdk-ea
 
 matrix:
   allow_failures:
     - jdk: openjdk-ea
-
-after_success:
-  - mvn clean cobertura:cobertura coveralls:report
diff --git a/httpclient5-cache/pom.xml b/httpclient5-cache/pom.xml
index 743d169..2a4b648 100644
--- a/httpclient5-cache/pom.xml
+++ b/httpclient5-cache/pom.xml
@@ -80,11 +80,6 @@
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>org.easymock</groupId>
-      <artifactId>easymock</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
       <groupId>org.apache.httpcomponents.client5</groupId>
       <artifactId>httpclient5</artifactId>
       <version>${project.version}</version>
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/AbstractProtocolTest.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/AbstractProtocolTest.java
deleted file mode 100644
index 67e75a2..0000000
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/AbstractProtocolTest.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-package org.apache.hc.client5.http.impl.cache;
-
-import java.io.IOException;
-import java.util.HashMap;
-
-import org.apache.hc.client5.http.HttpRoute;
-import org.apache.hc.client5.http.classic.ExecChain;
-import org.apache.hc.client5.http.classic.ExecChainHandler;
-import org.apache.hc.client5.http.classic.ExecRuntime;
-import org.apache.hc.client5.http.protocol.HttpClientContext;
-import org.apache.hc.core5.http.ClassicHttpRequest;
-import org.apache.hc.core5.http.ClassicHttpResponse;
-import org.apache.hc.core5.http.HttpEntity;
-import org.apache.hc.core5.http.HttpException;
-import org.apache.hc.core5.http.HttpHost;
-import org.apache.hc.core5.http.HttpRequest;
-import org.apache.hc.core5.http.HttpResponse;
-import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
-import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
-import org.easymock.EasyMock;
-import org.easymock.IExpectationSetters;
-import org.junit.Before;
-
-public abstract class AbstractProtocolTest {
-
-    protected static final int MAX_BYTES = 1024;
-    protected static final int MAX_ENTRIES = 100;
-    protected final int entityLength = 128;
-    protected HttpHost host;
-    protected HttpRoute route;
-    protected HttpEntity body;
-    protected HttpClientContext context;
-    protected ExecChain mockExecChain;
-    protected ExecRuntime mockExecRuntime;
-    protected HttpCache mockCache;
-    protected ClassicHttpRequest request;
-    protected ClassicHttpResponse originResponse;
-    protected CacheConfig config;
-    protected ExecChainHandler impl;
-    protected HttpCache cache;
-
-    public static ClassicHttpRequest eqRequest(final ClassicHttpRequest in) {
-        EasyMock.reportMatcher(new RequestEquivalent(in));
-        return null;
-    }
-
-    public static HttpResponse eqResponse(final HttpResponse in) {
-        EasyMock.reportMatcher(new ResponseEquivalent(in));
-        return null;
-    }
-
-    public static ClassicHttpResponse eqCloseableResponse(final ClassicHttpResponse in) {
-        EasyMock.reportMatcher(new ResponseEquivalent(in));
-        return null;
-    }
-
-    @Before
-    public void setUp() {
-        host = new HttpHost("foo.example.com", 80);
-
-        route = new HttpRoute(host);
-
-        body = HttpTestUtils.makeBody(entityLength);
-
-        request = new BasicClassicHttpRequest("GET", "/foo");
-
-        context = HttpClientContext.create();
-
-        originResponse = HttpTestUtils.make200Response();
-
-        config = CacheConfig.custom()
-            .setMaxCacheEntries(MAX_ENTRIES)
-            .setMaxObjectSize(MAX_BYTES)
-            .build();
-
-        cache = new BasicHttpCache(config);
-        mockExecChain = EasyMock.createNiceMock(ExecChain.class);
-        mockExecRuntime = EasyMock.createNiceMock(ExecRuntime.class);
-        mockCache = EasyMock.createNiceMock(HttpCache.class);
-        impl = createCachingExecChain(cache, config);
-    }
-
-    public ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException {
-        return impl.execute(
-                ClassicRequestBuilder.copy(request).build(),
-                new ExecChain.Scope("test", route, request, mockExecRuntime, context),
-                mockExecChain);
-    }
-
-    protected ExecChainHandler createCachingExecChain(final HttpCache cache, final CacheConfig config) {
-        return new CachingExec(cache, null, config);
-    }
-
-    protected boolean supportsRangeAndContentRangeHeaders(final ExecChainHandler impl) {
-        return impl instanceof CachingExec && ((CachingExec) impl).supportsRangeAndContentRangeHeaders();
-    }
-
-    protected void replayMocks() {
-        EasyMock.replay(mockExecChain);
-        EasyMock.replay(mockCache);
-    }
-
-    protected void verifyMocks() {
-        EasyMock.verify(mockExecChain);
-        EasyMock.verify(mockCache);
-    }
-
-    protected IExpectationSetters<ClassicHttpResponse> backendExpectsAnyRequest() throws Exception {
-        final ClassicHttpResponse resp = mockExecChain.proceed(
-                EasyMock.isA(ClassicHttpRequest.class),
-                EasyMock.isA(ExecChain.Scope.class));
-        return EasyMock.expect(resp);
-    }
-
-    protected IExpectationSetters<ClassicHttpResponse> backendExpectsAnyRequestAndReturn(
-            final ClassicHttpResponse response) throws Exception {
-        final ClassicHttpResponse resp = mockExecChain.proceed(
-                EasyMock.isA(ClassicHttpRequest.class),
-                EasyMock.isA(ExecChain.Scope.class));
-        return EasyMock.expect(resp).andReturn(response);
-    }
-
-    protected void emptyMockCacheExpectsNoPuts() throws Exception {
-        mockExecChain = EasyMock.createNiceMock(ExecChain.class);
-        mockCache = EasyMock.createNiceMock(HttpCache.class);
-
-        impl = new CachingExec(mockCache, null, config);
-
-        EasyMock.expect(mockCache.getCacheEntry(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class)))
-            .andReturn(null).anyTimes();
-        EasyMock.expect(mockCache.getVariantCacheEntriesWithEtags(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class)))
-            .andReturn(new HashMap<>()).anyTimes();
-
-        mockCache.flushCacheEntriesFor(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class));
-        EasyMock.expectLastCall().anyTimes();
-
-        mockCache.flushCacheEntriesFor(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class));
-        EasyMock.expectLastCall().anyTimes();
-
-        mockCache.flushCacheEntriesInvalidatedByRequest(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class));
-        EasyMock.expectLastCall().anyTimes();
-
-        mockCache.flushCacheEntriesInvalidatedByExchange(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class), EasyMock.isA(HttpResponse.class));
-        EasyMock.expectLastCall().anyTimes();
-    }
-
-    protected void behaveAsNonSharedCache() {
-        config = CacheConfig.custom()
-                .setMaxCacheEntries(MAX_ENTRIES)
-                .setMaxObjectSize(MAX_BYTES)
-                .setSharedCache(false)
-                .build();
-        impl = new CachingExec(cache, null, config);
-    }
-
-    public AbstractProtocolTest() {
-        super();
-    }
-
-}
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/DummyBackend.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/DummyBackend.java
deleted file mode 100644
index 4694e38..0000000
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/DummyBackend.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-package org.apache.hc.client5.http.impl.cache;
-
-import java.io.IOException;
-
-import org.apache.hc.client5.http.classic.ExecChain;
-import org.apache.hc.core5.http.ClassicHttpRequest;
-import org.apache.hc.core5.http.ClassicHttpResponse;
-import org.apache.hc.core5.http.HttpException;
-import org.apache.hc.core5.http.HttpRequest;
-import org.apache.hc.core5.http.HttpStatus;
-import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
-
-public class DummyBackend implements ExecChain {
-
-    private ClassicHttpRequest request;
-    private ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
-    private int executions;
-
-    public void setResponse(final ClassicHttpResponse resp) {
-        response = resp;
-    }
-
-    public HttpRequest getCapturedRequest() {
-        return request;
-    }
-
-    @Override
-    public ClassicHttpResponse proceed(
-            final ClassicHttpRequest request,
-            final Scope scope) throws IOException, HttpException {
-        this.request = request;
-        executions++;
-        return response;
-    }
-
-    public int getExecutions() {
-        return executions;
-    }
-}
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/HttpTestUtils.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/HttpTestUtils.java
index 9815aab..ea7ac0f 100644
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/HttpTestUtils.java
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/HttpTestUtils.java
@@ -251,7 +251,7 @@ public class HttpTestUtils {
     }
 
     public static HttpCacheEntry makeCacheEntry(final Date requestDate,
-            final Date responseDate, final Header[] headers) {
+            final Date responseDate, final Header... headers) {
         final byte[] bytes = getRandomBytes(128);
         return makeCacheEntry(requestDate, responseDate, headers, bytes);
     }
@@ -284,7 +284,7 @@ public class HttpTestUtils {
         return makeCacheEntry(getStockHeaders(new Date()), bytes);
     }
 
-    public static HttpCacheEntry makeCacheEntry(final Header[] headers) {
+    public static HttpCacheEntry makeCacheEntry(final Header... headers) {
         return makeCacheEntry(headers, getRandomBytes(128));
     }
 
@@ -293,27 +293,27 @@ public class HttpTestUtils {
         return makeCacheEntry(now, now);
     }
 
-    public static HttpCacheEntry makeCacheEntryWithNoRequestMethodOrEntity(final Header[] headers) {
+    public static HttpCacheEntry makeCacheEntryWithNoRequestMethodOrEntity(final Header... headers) {
         final Date now = new Date();
         return new HttpCacheEntry(now, now, HttpStatus.SC_OK, headers, null, null);
     }
 
-    public static HttpCacheEntry makeCacheEntryWithNoRequestMethod(final Header[] headers) {
+    public static HttpCacheEntry makeCacheEntryWithNoRequestMethod(final Header... headers) {
         final Date now = new Date();
         return new HttpCacheEntry(now, now, HttpStatus.SC_OK, headers, new HeapResource(getRandomBytes(128)), null);
     }
 
-    public static HttpCacheEntry make204CacheEntryWithNoRequestMethod(final Header[] headers) {
+    public static HttpCacheEntry make204CacheEntryWithNoRequestMethod(final Header... headers) {
         final Date now = new Date();
         return new HttpCacheEntry(now, now, HttpStatus.SC_NO_CONTENT, headers, null, null);
     }
 
-    public static HttpCacheEntry makeHeadCacheEntry(final Header[] headers) {
+    public static HttpCacheEntry makeHeadCacheEntry(final Header... headers) {
         final Date now = new Date();
         return new HttpCacheEntry(now, now, HttpStatus.SC_OK, headers, null, null);
     }
 
-    public static HttpCacheEntry makeHeadCacheEntryWithNoRequestMethod(final Header[] headers) {
+    public static HttpCacheEntry makeHeadCacheEntryWithNoRequestMethod(final Header... headers) {
         final Date now = new Date();
         return new HttpCacheEntry(now, now, HttpStatus.SC_OK, headers, null, null);
     }
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/RequestEquivalent.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/RequestEquivalent.java
index bd0c6e9..769539e 100644
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/RequestEquivalent.java
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/RequestEquivalent.java
@@ -27,30 +27,27 @@
 package org.apache.hc.client5.http.impl.cache;
 
 import org.apache.hc.core5.http.HttpRequest;
-import org.easymock.IArgumentMatcher;
+import org.mockito.ArgumentMatcher;
+import org.mockito.ArgumentMatchers;
 
-public class RequestEquivalent implements IArgumentMatcher {
+public class RequestEquivalent<T extends HttpRequest> implements ArgumentMatcher<T> {
 
-    private final HttpRequest expected;
+    private final T expected;
 
-    public RequestEquivalent(final HttpRequest expected) {
+    public RequestEquivalent(final T expected) {
         this.expected = expected;
     }
 
     @Override
-    public boolean matches(final Object actual) {
-        if (!(actual instanceof HttpRequest)) {
+    public boolean matches(final T argument) {
+        if (argument == null) {
             return false;
         }
-        final HttpRequest other = (HttpRequest) actual;
-        return HttpTestUtils.equivalent(expected, other);
+        return HttpTestUtils.equivalent(expected, argument);
     }
 
-    @Override
-    public void appendTo(final StringBuffer buf) {
-        buf.append("eqRequest(");
-        buf.append(expected);
-        buf.append(")");
+    public static <T extends HttpRequest> T eq(final T request) {
+        return ArgumentMatchers.argThat(new RequestEquivalent<>(request));
     }
 
 }
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/ResponseEquivalent.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/ResponseEquivalent.java
index fb0c3c3..0cdc9fc 100644
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/ResponseEquivalent.java
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/ResponseEquivalent.java
@@ -27,30 +27,27 @@
 package org.apache.hc.client5.http.impl.cache;
 
 import org.apache.hc.core5.http.HttpResponse;
-import org.easymock.IArgumentMatcher;
+import org.mockito.ArgumentMatcher;
+import org.mockito.ArgumentMatchers;
 
-public class ResponseEquivalent implements IArgumentMatcher {
+public class ResponseEquivalent<T extends HttpResponse> implements ArgumentMatcher<T> {
 
-    private final HttpResponse expected;
+    private final T expected;
 
-    public ResponseEquivalent(final HttpResponse expected) {
+    public ResponseEquivalent(final T expected) {
         this.expected = expected;
     }
 
     @Override
-    public boolean matches(final Object actual) {
-        if (!(actual instanceof HttpResponse)) {
+    public boolean matches(final T argument) {
+        if (argument == null) {
             return false;
         }
-        final HttpResponse other = (HttpResponse) actual;
-        return HttpTestUtils.equivalent(expected, other);
+        return HttpTestUtils.equivalent(expected, argument);
     }
 
-    @Override
-    public void appendTo(final StringBuffer buf) {
-        buf.append("eqRequest(");
-        buf.append(expected);
-        buf.append(")");
+    public static <T extends HttpResponse> T eq(final T response) {
+        return ArgumentMatchers.argThat(new ResponseEquivalent<>(response));
     }
 
 }
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCachingExec.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCachingExec.java
deleted file mode 100644
index 8ec3db6..0000000
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCachingExec.java
+++ /dev/null
@@ -1,458 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-package org.apache.hc.client5.http.impl.cache;
-
-import static org.easymock.EasyMock.anyObject;
-import static org.easymock.EasyMock.eq;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.isA;
-import static org.easymock.EasyMock.same;
-import static org.easymock.EasyMock.createMockBuilder;
-import static org.easymock.EasyMock.createNiceMock;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.verify;
-import static org.junit.Assert.assertTrue;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
-import org.apache.hc.client5.http.cache.HttpCacheEntry;
-import org.apache.hc.client5.http.classic.ExecChain;
-import org.apache.hc.client5.http.classic.methods.HttpGet;
-import org.apache.hc.client5.http.utils.DateUtils;
-import org.apache.hc.core5.http.ClassicHttpRequest;
-import org.apache.hc.core5.http.ClassicHttpResponse;
-import org.apache.hc.core5.http.HeaderElements;
-import org.apache.hc.core5.http.HttpHeaders;
-import org.apache.hc.core5.http.HttpHost;
-import org.apache.hc.core5.http.HttpRequest;
-import org.apache.hc.core5.http.HttpResponse;
-import org.apache.hc.core5.http.HttpStatus;
-import org.apache.hc.core5.http.HttpVersion;
-import org.apache.hc.core5.http.io.entity.InputStreamEntity;
-import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
-import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
-import org.easymock.EasyMock;
-import org.easymock.IExpectationSetters;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-@SuppressWarnings("boxing") // test code
-public class TestCachingExec extends TestCachingExecChain {
-
-    private static final String GET_CURRENT_DATE = "getCurrentDate";
-
-    private static final String HANDLE_BACKEND_RESPONSE = "handleBackendResponse";
-
-    private static final String CALL_BACKEND = "callBackend";
-
-    private static final String REVALIDATE_CACHE_ENTRY = "revalidateCacheEntry";
-
-    private CachingExec impl;
-    private boolean mockedImpl;
-
-    private ExecChain.Scope scope;
-    private ClassicHttpResponse mockBackendResponse;
-
-    private Date requestDate;
-    private Date responseDate;
-
-    @Override
-    @Before
-    public void setUp() {
-        super.setUp();
-
-        scope = new ExecChain.Scope("test", route, request, mockEndpoint, context);
-        mockBackendResponse = createNiceMock(ClassicHttpResponse.class);
-
-        requestDate = new Date(System.currentTimeMillis() - 1000);
-        responseDate = new Date();
-    }
-
-    @Override
-    public CachingExec createCachingExecChain(
-            final HttpCache mockCache, final CacheValidityPolicy mockValidityPolicy,
-            final ResponseCachingPolicy mockResponsePolicy,
-            final CachedHttpResponseGenerator mockResponseGenerator,
-            final CacheableRequestPolicy mockRequestPolicy,
-            final CachedResponseSuitabilityChecker mockSuitabilityChecker,
-            final ResponseProtocolCompliance mockResponseProtocolCompliance,
-            final RequestProtocolCompliance mockRequestProtocolCompliance,
-            final DefaultCacheRevalidator mockCacheRevalidator,
-            final ConditionalRequestBuilder<ClassicHttpRequest> mockConditionalRequestBuilder,
-            final CacheConfig config) {
-        return impl = new CachingExec(
-                mockCache,
-                mockValidityPolicy,
-                mockResponsePolicy,
-                mockResponseGenerator,
-                mockRequestPolicy,
-                mockSuitabilityChecker,
-                mockResponseProtocolCompliance,
-                mockRequestProtocolCompliance,
-                mockCacheRevalidator,
-                mockConditionalRequestBuilder,
-                config);
-    }
-
-    @Override
-    public CachingExec createCachingExecChain(final HttpCache cache, final CacheConfig config) {
-        return impl = new CachingExec(cache, null, config);
-    }
-
-    @Override
-    protected void replayMocks() {
-        super.replayMocks();
-        replay(mockBackendResponse);
-        if (mockedImpl) {
-            replay(impl);
-        }
-    }
-
-    @Override
-    protected void verifyMocks() {
-        super.verifyMocks();
-        verify(mockBackendResponse);
-        if (mockedImpl) {
-            verify(impl);
-        }
-    }
-
-
-    @Test
-    public void testRequestThatCannotBeServedFromCacheCausesBackendRequest() throws Exception {
-        cacheInvalidatorWasCalled();
-        requestPolicyAllowsCaching(false);
-        mockImplMethods(CALL_BACKEND);
-
-        implExpectsAnyRequestAndReturn(mockBackendResponse);
-        requestIsFatallyNonCompliant(null);
-
-        replayMocks();
-        final HttpResponse result = impl.execute(request, scope, mockExecChain);
-        verifyMocks();
-
-        Assert.assertSame(mockBackendResponse, result);
-    }
-
-    @Test
-    public void testCacheMissCausesBackendRequest() throws Exception {
-        mockImplMethods(CALL_BACKEND);
-        requestPolicyAllowsCaching(true);
-        getCacheEntryReturns(null);
-        getVariantCacheEntriesReturns(new HashMap<>());
-
-        requestIsFatallyNonCompliant(null);
-
-        implExpectsAnyRequestAndReturn(mockBackendResponse);
-
-        replayMocks();
-        final HttpResponse result = impl.execute(request, scope, mockExecChain);
-        verifyMocks();
-
-        Assert.assertSame(mockBackendResponse, result);
-        Assert.assertEquals(1, impl.getCacheMisses());
-        Assert.assertEquals(0, impl.getCacheHits());
-        Assert.assertEquals(0, impl.getCacheUpdates());
-    }
-
-    @Test
-    public void testUnsuitableUnvalidatableCacheEntryCausesBackendRequest() throws Exception {
-        mockImplMethods(CALL_BACKEND);
-        requestPolicyAllowsCaching(true);
-        requestIsFatallyNonCompliant(null);
-
-        getCacheEntryReturns(mockCacheEntry);
-        cacheEntrySuitable(false);
-        cacheEntryValidatable(false);
-        expect(mockConditionalRequestBuilder.buildConditionalRequest(request, mockCacheEntry))
-            .andReturn(request);
-        backendExpectsRequestAndReturn(request, mockBackendResponse);
-        expect(mockBackendResponse.getVersion()).andReturn(HttpVersion.HTTP_1_1).anyTimes();
-        expect(mockBackendResponse.getCode()).andReturn(200);
-
-        replayMocks();
-        final HttpResponse result = impl.execute(request, scope, mockExecChain);
-        verifyMocks();
-
-        Assert.assertSame(mockBackendResponse, result);
-        Assert.assertEquals(0, impl.getCacheMisses());
-        Assert.assertEquals(1, impl.getCacheHits());
-        Assert.assertEquals(1, impl.getCacheUpdates());
-    }
-
-    @Test
-    public void testUnsuitableValidatableCacheEntryCausesRevalidation() throws Exception {
-        mockImplMethods(REVALIDATE_CACHE_ENTRY);
-        requestPolicyAllowsCaching(true);
-        requestIsFatallyNonCompliant(null);
-
-        getCacheEntryReturns(mockCacheEntry);
-        cacheEntrySuitable(false);
-        cacheEntryValidatable(true);
-        cacheEntryMustRevalidate(false);
-        cacheEntryProxyRevalidate(false);
-        mayReturnStaleWhileRevalidating(false);
-
-        expect(impl.revalidateCacheEntry(
-                isA(HttpHost.class),
-                isA(ClassicHttpRequest.class),
-                isA(ExecChain.Scope.class),
-                isA(ExecChain.class),
-                isA(HttpCacheEntry.class))).andReturn(mockBackendResponse);
-
-        replayMocks();
-        final HttpResponse result = impl.execute(request, scope, mockExecChain);
-        verifyMocks();
-
-        Assert.assertSame(mockBackendResponse, result);
-        Assert.assertEquals(0, impl.getCacheMisses());
-        Assert.assertEquals(1, impl.getCacheHits());
-        Assert.assertEquals(0, impl.getCacheUpdates());
-    }
-
-    @Test
-    public void testRevalidationCallsHandleBackEndResponseWhenNot200Or304() throws Exception {
-        mockImplMethods(GET_CURRENT_DATE, HANDLE_BACKEND_RESPONSE);
-
-        final ClassicHttpRequest validate = new BasicClassicHttpRequest("GET", "/");
-        final ClassicHttpResponse originResponse = new BasicClassicHttpResponse(HttpStatus.SC_NOT_FOUND, "Not Found");
-        final ClassicHttpResponse finalResponse =  HttpTestUtils.make200Response();
-
-        conditionalRequestBuilderReturns(validate);
-        getCurrentDateReturns(requestDate);
-        backendExpectsRequestAndReturn(validate, originResponse);
-        getCurrentDateReturns(responseDate);
-        expect(impl.handleBackendResponse(
-                same(host),
-                same(validate),
-                same(scope),
-                eq(requestDate),
-                eq(responseDate),
-                same(originResponse))).andReturn(finalResponse);
-
-        replayMocks();
-        final HttpResponse result =
-            impl.revalidateCacheEntry(host, request, scope, mockExecChain, entry);
-        verifyMocks();
-
-        Assert.assertSame(finalResponse, result);
-    }
-
-    @Test
-    public void testRevalidationUpdatesCacheEntryAndPutsItToCacheWhen304ReturningCachedResponse()
-            throws Exception {
-
-        mockImplMethods(GET_CURRENT_DATE);
-
-        final ClassicHttpRequest validate = new BasicClassicHttpRequest("GET", "/");
-        final ClassicHttpResponse originResponse = HttpTestUtils.make304Response();
-        final HttpCacheEntry updatedEntry = HttpTestUtils.makeCacheEntry();
-
-        conditionalRequestBuilderReturns(validate);
-        getCurrentDateReturns(requestDate);
-        backendExpectsRequestAndReturn(validate, originResponse);
-        getCurrentDateReturns(responseDate);
-        expect(mockCache.updateCacheEntry(
-                eq(host),
-                same(request),
-                same(entry),
-                same(originResponse),
-                eq(requestDate),
-                eq(responseDate)))
-            .andReturn(updatedEntry);
-        expect(mockSuitabilityChecker.isConditional(request)).andReturn(false);
-        responseIsGeneratedFromCache(SimpleHttpResponse.create(HttpStatus.SC_OK));
-
-        replayMocks();
-        impl.revalidateCacheEntry(host, request, scope, mockExecChain, entry);
-        verifyMocks();
-    }
-
-    @Test
-    public void testRevalidationRewritesAbsoluteUri() throws Exception {
-
-        mockImplMethods(GET_CURRENT_DATE);
-
-        // Fail on an unexpected request, rather than causing a later NPE
-        EasyMock.resetToStrict(mockExecChain);
-
-        final ClassicHttpRequest validate = new HttpGet("http://foo.example.com/resource");
-        final ClassicHttpRequest relativeValidate = new BasicClassicHttpRequest("GET", "/resource");
-        final ClassicHttpResponse originResponse = new BasicClassicHttpResponse(HttpStatus.SC_OK, "Okay");
-
-        conditionalRequestBuilderReturns(validate);
-        getCurrentDateReturns(requestDate);
-
-        final ClassicHttpResponse resp = mockExecChain.proceed(
-                eqRequest(relativeValidate), isA(ExecChain.Scope.class));
-        expect(resp).andReturn(originResponse);
-
-        getCurrentDateReturns(responseDate);
-
-        replayMocks();
-        impl.revalidateCacheEntry(host, request, scope, mockExecChain, entry);
-        verifyMocks();
-    }
-
-    @Test
-    public void testEndlessResponsesArePassedThrough() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
-
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
-        resp1.setHeader("Date", DateUtils.formatDate(new Date()));
-        resp1.setHeader("Server", "MockOrigin/1.0");
-        resp1.setHeader(HttpHeaders.TRANSFER_ENCODING, HeaderElements.CHUNKED_ENCODING);
-
-        final AtomicInteger size = new AtomicInteger();
-        final AtomicInteger maxlength = new AtomicInteger(Integer.MAX_VALUE);
-        resp1.setEntity(new InputStreamEntity(new InputStream() {
-            private Throwable closed;
-
-            @Override
-            public void close() throws IOException {
-                closed = new Throwable();
-                super.close();
-            }
-
-            @Override
-            public int read() throws IOException {
-                Thread.yield();
-                if (closed != null) {
-                    throw new IOException("Response has been closed");
-
-                }
-                if (size.incrementAndGet() > maxlength.get()) {
-                    return -1;
-                }
-                return 'y';
-            }
-        }, -1, null));
-
-        final ClassicHttpResponse resp = mockExecChain.proceed(
-                isA(ClassicHttpRequest.class), isA(ExecChain.Scope.class));
-        EasyMock.expect(resp).andReturn(resp1);
-
-        final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest();
-
-        replayMocks();
-        final ClassicHttpResponse resp2 = impl.execute(req1, scope, mockExecChain);
-        maxlength.set(size.get() * 2);
-        verifyMocks();
-        assertTrue(HttpTestUtils.semanticallyTransparent(resp1, resp2));
-    }
-
-    @Test
-    public void testCallBackendMakesBackEndRequestAndHandlesResponse() throws Exception {
-        mockImplMethods(GET_CURRENT_DATE, HANDLE_BACKEND_RESPONSE);
-        final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
-        getCurrentDateReturns(requestDate);
-        backendExpectsRequestAndReturn(request, resp);
-        getCurrentDateReturns(responseDate);
-        handleBackendResponseReturnsResponse(request, resp);
-
-        replayMocks();
-
-        impl.callBackend(host, request, scope, mockExecChain);
-
-        verifyMocks();
-    }
-
-    @Test
-    public void testDoesNotFlushCachesOnCacheHit() throws Exception {
-        requestPolicyAllowsCaching(true);
-        requestIsFatallyNonCompliant(null);
-
-        getCacheEntryReturns(mockCacheEntry);
-        doesNotFlushCache();
-        cacheEntrySuitable(true);
-        cacheEntryValidatable(true);
-
-        responseIsGeneratedFromCache(SimpleHttpResponse.create(HttpStatus.SC_OK));
-
-        replayMocks();
-        impl.execute(request, scope, mockExecChain);
-        verifyMocks();
-    }
-
-    private IExpectationSetters<ClassicHttpResponse> implExpectsAnyRequestAndReturn(
-            final ClassicHttpResponse response) throws Exception {
-        final ClassicHttpResponse resp = impl.callBackend(
-                same(host),
-                isA(ClassicHttpRequest.class),
-                isA(ExecChain.Scope.class),
-                isA(ExecChain.class));
-        return EasyMock.expect(resp).andReturn(response);
-    }
-
-    private void getVariantCacheEntriesReturns(final Map<String,Variant> result) {
-        expect(mockCache.getVariantCacheEntriesWithEtags(host, request)).andReturn(result);
-    }
-
-    private void cacheInvalidatorWasCalled() {
-        mockCache.flushCacheEntriesInvalidatedByRequest((HttpHost)anyObject(), (HttpRequest)anyObject());
-    }
-
-    private void getCurrentDateReturns(final Date date) {
-        expect(impl.getCurrentDate()).andReturn(date);
-    }
-
-    private void handleBackendResponseReturnsResponse(final ClassicHttpRequest request, final ClassicHttpResponse response)
-            throws IOException {
-        expect(
-                impl.handleBackendResponse(
-                        same(host),
-                        same(request),
-                        same(scope),
-                        isA(Date.class),
-                        isA(Date.class),
-                        isA(ClassicHttpResponse.class))).andReturn(response);
-    }
-
-    private void mockImplMethods(final String... methods) {
-        mockedImpl = true;
-        impl = createMockBuilder(CachingExec.class).withConstructor(
-                mockCache,
-                mockValidityPolicy,
-                mockResponsePolicy,
-                mockResponseGenerator,
-                mockRequestPolicy,
-                mockSuitabilityChecker,
-                mockResponseProtocolCompliance,
-                mockRequestProtocolCompliance,
-                mockCacheRevalidator,
-                mockConditionalRequestBuilder,
-                config).addMockedMethods(methods).createNiceMock();
-    }
-
-}
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCachingExecChain.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCachingExecChain.java
index 842d200..7074989 100644
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCachingExecChain.java
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCachingExecChain.java
@@ -26,27 +26,13 @@
  */
 package org.apache.hc.client5.http.impl.cache;
 
-import static org.easymock.EasyMock.anyObject;
-import static org.easymock.EasyMock.createNiceMock;
-import static org.easymock.EasyMock.eq;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.expectLastCall;
-import static org.easymock.EasyMock.isA;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.same;
-import static org.easymock.EasyMock.verify;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.SocketException;
 import java.net.SocketTimeoutException;
-import java.util.ArrayList;
 import java.util.Date;
-import java.util.List;
 
 import org.apache.hc.client5.http.HttpRoute;
 import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
@@ -63,82 +49,49 @@ import org.apache.hc.client5.http.protocol.HttpClientContext;
 import org.apache.hc.client5.http.utils.DateUtils;
 import org.apache.hc.core5.http.ClassicHttpRequest;
 import org.apache.hc.core5.http.ClassicHttpResponse;
-import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HttpException;
 import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.HttpRequest;
-import org.apache.hc.core5.http.HttpResponse;
 import org.apache.hc.core5.http.HttpStatus;
 import org.apache.hc.core5.http.HttpVersion;
-import org.apache.hc.core5.http.io.HttpClientResponseHandler;
 import org.apache.hc.core5.http.io.entity.EntityUtils;
 import org.apache.hc.core5.http.io.entity.InputStreamEntity;
 import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
 import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
 import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
-import org.apache.hc.core5.http.message.BasicHeader;
 import org.apache.hc.core5.net.URIAuthority;
-import org.apache.hc.core5.util.ByteArrayBuffer;
-import org.apache.hc.core5.util.TimeValue;
-import org.easymock.Capture;
-import org.easymock.EasyMock;
-import org.easymock.IExpectationSetters;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class TestCachingExecChain {
+
+    @Mock
+    ExecChain mockExecChain;
+    @Mock
+    ExecRuntime mockExecRuntime;
+    @Mock
+    HttpCacheStorage mockStorage;
+    @Spy
+    HttpCache cache = new BasicHttpCache();
+
+    CacheConfig config;
+    HttpRoute route;
+    HttpHost host;
+    ClassicHttpRequest request;
+    HttpCacheContext context;
+    HttpCacheEntry entry;
+    CachingExec impl;
 
-import junit.framework.AssertionFailedError;
-
-@SuppressWarnings("boxing") // test code
-public abstract class TestCachingExecChain {
-
-    private CachingExec impl;
-
-    protected CacheValidityPolicy mockValidityPolicy;
-    protected CacheableRequestPolicy mockRequestPolicy;
-    protected ExecChain mockExecChain;
-    protected ExecRuntime mockEndpoint;
-    protected HttpCache mockCache;
-    private HttpCacheStorage mockStorage;
-    protected CachedResponseSuitabilityChecker mockSuitabilityChecker;
-    protected ResponseCachingPolicy mockResponsePolicy;
-    protected HttpCacheEntry mockCacheEntry;
-    protected CachedHttpResponseGenerator mockResponseGenerator;
-    private HttpClientResponseHandler<Object> mockHandler;
-    private ClassicHttpRequest mockUriRequest;
-    private HttpRequest mockConditionalRequest;
-    protected ResponseProtocolCompliance mockResponseProtocolCompliance;
-    protected RequestProtocolCompliance mockRequestProtocolCompliance;
-    protected DefaultCacheRevalidator mockCacheRevalidator;
-    protected ConditionalRequestBuilder<ClassicHttpRequest> mockConditionalRequestBuilder;
-    protected CacheConfig config;
-
-    protected HttpRoute route;
-    protected HttpHost host;
-    protected ClassicHttpRequest request;
-    protected HttpCacheContext context;
-    protected HttpCacheEntry entry;
-
-    @SuppressWarnings("unchecked")
     @Before
     public void setUp() {
-        mockRequestPolicy = createNiceMock(CacheableRequestPolicy.class);
-        mockValidityPolicy = createNiceMock(CacheValidityPolicy.class);
-        mockEndpoint = createNiceMock(ExecRuntime.class);
-        mockExecChain = createNiceMock(ExecChain.class);
-        mockCache = createNiceMock(HttpCache.class);
-        mockSuitabilityChecker = createNiceMock(CachedResponseSuitabilityChecker.class);
-        mockResponsePolicy = createNiceMock(ResponseCachingPolicy.class);
-        mockHandler = createNiceMock(HttpClientResponseHandler.class);
-        mockUriRequest = createNiceMock(ClassicHttpRequest.class);
-        mockCacheEntry = createNiceMock(HttpCacheEntry.class);
-        mockResponseGenerator = createNiceMock(CachedHttpResponseGenerator.class);
-        mockConditionalRequest = createNiceMock(HttpRequest.class);
-        mockResponseProtocolCompliance = createNiceMock(ResponseProtocolCompliance.class);
-        mockRequestProtocolCompliance = createNiceMock(RequestProtocolCompliance.class);
-        mockCacheRevalidator = createNiceMock(DefaultCacheRevalidator.class);
-        mockConditionalRequestBuilder = createNiceMock(ConditionalRequestBuilder.class);
-        mockStorage = createNiceMock(HttpCacheStorage.class);
         config = CacheConfig.DEFAULT;
 
         host = new HttpHost("foo.example.com", 80);
@@ -146,98 +99,35 @@ public abstract class TestCachingExecChain {
         request = new BasicClassicHttpRequest("GET", "/stuff");
         context = HttpCacheContext.create();
         entry = HttpTestUtils.makeCacheEntry();
-        impl = createCachingExecChain(mockCache, mockValidityPolicy,
-                mockResponsePolicy, mockResponseGenerator, mockRequestPolicy, mockSuitabilityChecker,
-                mockResponseProtocolCompliance,mockRequestProtocolCompliance,
-                mockCacheRevalidator, mockConditionalRequestBuilder, config);
-    }
-
-    public abstract CachingExec createCachingExecChain(
-            HttpCache responseCache, CacheValidityPolicy validityPolicy,
-            ResponseCachingPolicy responseCachingPolicy, CachedHttpResponseGenerator responseGenerator,
-            CacheableRequestPolicy cacheableRequestPolicy,
-            CachedResponseSuitabilityChecker suitabilityChecker,
-            ResponseProtocolCompliance responseCompliance, RequestProtocolCompliance requestCompliance,
-            DefaultCacheRevalidator cacheRevalidator,
-            ConditionalRequestBuilder<ClassicHttpRequest> conditionalRequestBuilder,
-            CacheConfig config);
-
-    public abstract CachingExec createCachingExecChain(HttpCache cache, CacheConfig config);
-
-    protected ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException {
-        return impl.execute(
-                ClassicRequestBuilder.copy(request).build(),
-                new ExecChain.Scope("test", route, request, mockEndpoint, context),
-                mockExecChain);
-    }
 
-    public static ClassicHttpRequest eqRequest(final ClassicHttpRequest in) {
-        EasyMock.reportMatcher(new RequestEquivalent(in));
-        return null;
+        impl = new CachingExec(cache, null, CacheConfig.DEFAULT);
     }
 
-    public static <R extends HttpResponse> R eqResponse(final R in) {
-        EasyMock.reportMatcher(new ResponseEquivalent(in));
-        return null;
-    }
-
-    protected void replayMocks() {
-        replay(mockRequestPolicy);
-        replay(mockValidityPolicy);
-        replay(mockSuitabilityChecker);
-        replay(mockResponsePolicy);
-        replay(mockCacheEntry);
-        replay(mockResponseGenerator);
-        replay(mockExecChain);
-        replay(mockCache);
-        replay(mockHandler);
-        replay(mockUriRequest);
-        replay(mockConditionalRequestBuilder);
-        replay(mockConditionalRequest);
-        replay(mockResponseProtocolCompliance);
-        replay(mockRequestProtocolCompliance);
-        replay(mockStorage);
-    }
-
-    protected void verifyMocks() {
-        verify(mockRequestPolicy);
-        verify(mockValidityPolicy);
-        verify(mockSuitabilityChecker);
-        verify(mockResponsePolicy);
-        verify(mockCacheEntry);
-        verify(mockResponseGenerator);
-        verify(mockExecChain);
-        verify(mockCache);
-        verify(mockHandler);
-        verify(mockUriRequest);
-        verify(mockConditionalRequestBuilder);
-        verify(mockConditionalRequest);
-        verify(mockResponseProtocolCompliance);
-        verify(mockRequestProtocolCompliance);
-        verify(mockStorage);
+    public ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException {
+        final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, mockExecRuntime, context);
+        return impl.execute(ClassicRequestBuilder.copy(request).build(), scope, mockExecChain);
     }
 
     @Test
     public void testCacheableResponsesGoIntoCache() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
-
         final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest();
         final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
         resp1.setHeader("Cache-Control", "max-age=3600");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
 
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
+
         execute(req1);
         execute(req2);
-        verifyMocks();
+
+        Mockito.verify(mockExecChain).proceed(Mockito.any(), Mockito.any());
+        Mockito.verify(cache).createCacheEntry(Mockito.eq(host), RequestEquivalent.eq(req1),
+                Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
     }
 
     @Test
     public void testOlderCacheableResponsesDoNotGoIntoCache() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final Date now = new Date();
         final Date fiveSecondsAgo = new Date(now.getTime() - 5 * 1000L);
 
@@ -247,8 +137,6 @@ public abstract class TestCachingExecChain {
         resp1.setHeader("Cache-Control", "max-age=3600");
         resp1.setHeader("Etag", "\"new-etag\"");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control", "no-cache");
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
@@ -256,22 +144,22 @@ public abstract class TestCachingExecChain {
         resp2.setHeader("Date", DateUtils.formatDate(fiveSecondsAgo));
         resp2.setHeader("Cache-Control", "max-age=3600");
 
-        backendExpectsAnyRequestAndReturn(resp2);
-
         final ClassicHttpRequest req3 = HttpTestUtils.makeDefaultRequest();
 
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
+
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         execute(req2);
         final ClassicHttpResponse result = execute(req3);
-        verifyMocks();
 
-        assertEquals("\"new-etag\"", result.getFirstHeader("ETag").getValue());
+        Assert.assertEquals("\"new-etag\"", result.getFirstHeader("ETag").getValue());
     }
 
     @Test
     public void testNewerCacheableResponsesReplaceExistingCacheEntry() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final Date now = new Date();
         final Date fiveSecondsAgo = new Date(now.getTime() - 5 * 1000L);
 
@@ -281,8 +169,6 @@ public abstract class TestCachingExecChain {
         resp1.setHeader("Cache-Control", "max-age=3600");
         resp1.setHeader("Etag", "\"old-etag\"");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control", "max-age=0");
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
@@ -290,119 +176,74 @@ public abstract class TestCachingExecChain {
         resp2.setHeader("Date", DateUtils.formatDate(now));
         resp2.setHeader("Cache-Control", "max-age=3600");
 
-        backendExpectsAnyRequestAndReturn(resp2);
-
         final ClassicHttpRequest req3 = HttpTestUtils.makeDefaultRequest();
 
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
+
         execute(req1);
-        execute(req2);
-        final ClassicHttpResponse result = execute(req3);
-        verifyMocks();
 
-        assertEquals("\"new-etag\"", result.getFirstHeader("ETag").getValue());
-    }
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
-    protected void requestIsFatallyNonCompliant(final RequestProtocolError error) {
-        final List<RequestProtocolError> errors = new ArrayList<>();
-        if (error != null) {
-            errors.add(error);
-        }
-        expect(mockRequestProtocolCompliance.requestIsFatallyNonCompliant(eqRequest(request)))
-            .andReturn(errors);
-    }
+        execute(req2);
+        final ClassicHttpResponse result = execute(req3);
 
-    @Test
-    public void testSuitableCacheEntryDoesNotCauseBackendRequest() throws Exception {
-        requestPolicyAllowsCaching(true);
-        getCacheEntryReturns(mockCacheEntry);
-        cacheEntrySuitable(true);
-        responseIsGeneratedFromCache(SimpleHttpResponse.create(HttpStatus.SC_OK));
-        requestIsFatallyNonCompliant(null);
-        entryHasStaleness(TimeValue.ZERO_MILLISECONDS);
-
-        replayMocks();
-        final ClassicHttpResponse result = execute(request);
-        verifyMocks();
+        Assert.assertEquals("\"new-etag\"", result.getFirstHeader("ETag").getValue());
     }
 
     @Test
     public void testNonCacheableResponseIsNotCachedAndIsReturnedAsIs() throws Exception {
-        final CacheConfig configDefault = CacheConfig.DEFAULT;
-        impl = createCachingExecChain(new BasicHttpCache(new HeapResourceFactory(),
-            mockStorage), configDefault);
+        final HttpCache cache = new BasicHttpCache(new HeapResourceFactory(), mockStorage);
+        impl = new CachingExec(cache, null, CacheConfig.DEFAULT);
 
         final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest();
         final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
         resp1.setHeader("Cache-Control", "no-cache");
 
-        expect(mockStorage.getEntry(isA(String.class))).andReturn(null).anyTimes();
-        mockStorage.removeEntry(isA(String.class));
-        expectLastCall().anyTimes();
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockStorage.getEntry(Mockito.any())).thenReturn(null);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(req1);
-        verifyMocks();
 
-        assertTrue(HttpTestUtils.semanticallyTransparent(resp1, result));
-    }
+        Assert.assertTrue(HttpTestUtils.semanticallyTransparent(resp1, result));
 
-    @Test
-    public void testResponseIsGeneratedWhenCacheEntryIsUsable() throws Exception {
-
-        requestIsFatallyNonCompliant(null);
-        requestPolicyAllowsCaching(true);
-        cacheEntrySuitable(true);
-        getCacheEntryReturns(mockCacheEntry);
-        responseIsGeneratedFromCache(SimpleHttpResponse.create(HttpStatus.SC_OK));
-        entryHasStaleness(TimeValue.ZERO_MILLISECONDS);
-
-        replayMocks();
-        execute(request);
-        verifyMocks();
+        Mockito.verify(mockStorage, Mockito.never()).putEntry(Mockito.any(), Mockito.any());
     }
 
     @Test
     public void testSetsModuleGeneratedResponseContextForCacheOptionsResponse() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req = new BasicClassicHttpRequest("OPTIONS", "*");
         req.setHeader("Max-Forwards", "0");
 
         execute(req);
-        Assert.assertEquals(CacheResponseStatus.CACHE_MODULE_RESPONSE,
-            context.getCacheResponseStatus());
+        Assert.assertEquals(CacheResponseStatus.CACHE_MODULE_RESPONSE, context.getCacheResponseStatus());
     }
 
     @Test
     public void testSetsModuleGeneratedResponseContextForFatallyNoncompliantRequest() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req = new HttpGet("http://foo.example.com/");
         req.setHeader("Range", "bytes=0-50");
         req.setHeader("If-Range", "W/\"weak-etag\"");
 
         execute(req);
-        Assert.assertEquals(CacheResponseStatus.CACHE_MODULE_RESPONSE,
-            context.getCacheResponseStatus());
+        Assert.assertEquals(CacheResponseStatus.CACHE_MODULE_RESPONSE, context.getCacheResponseStatus());
     }
 
     @Test
     public void testRecordsClientProtocolInViaHeaderIfRequestNotServableFromCache() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest originalRequest = new BasicClassicHttpRequest("GET", "/");
         originalRequest.setVersion(HttpVersion.HTTP_1_0);
         final ClassicHttpRequest req = originalRequest;
         req.setHeader("Cache-Control", "no-cache");
         final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content");
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
 
-        backendCaptureRequestAndReturn(cap, resp);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp);
 
-        replayMocks();
         execute(req);
-        verifyMocks();
 
-        final HttpRequest captured = cap.getValue();
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any());
+
+        final HttpRequest captured = reqCapture.getValue();
         final String via = captured.getFirstHeader("Via").getValue();
         final String proto = via.split("\\s+")[0];
         Assert.assertTrue("http/1.0".equalsIgnoreCase(proto) || "1.0".equalsIgnoreCase(proto));
@@ -410,95 +251,77 @@ public abstract class TestCachingExecChain {
 
     @Test
     public void testSetsCacheMissContextIfRequestNotServableFromCache() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req = new HttpGet("http://foo.example.com/");
         req.setHeader("Cache-Control", "no-cache");
         final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content");
 
-        backendExpectsAnyRequestAndReturn(resp);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp);
 
-        replayMocks();
         execute(req);
-        verifyMocks();
         Assert.assertEquals(CacheResponseStatus.CACHE_MISS, context.getCacheResponseStatus());
     }
 
     @Test
     public void testSetsViaHeaderOnResponseIfRequestNotServableFromCache() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req = new HttpGet("http://foo.example.com/");
         req.setHeader("Cache-Control", "no-cache");
         final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content");
 
-        backendExpectsAnyRequestAndReturn(resp);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(req);
-        verifyMocks();
         Assert.assertNotNull(result.getFirstHeader("Via"));
     }
 
     @Test
     public void testSetsViaHeaderOnResponseForCacheMiss() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("ETag", "\"etag\"");
         resp1.setHeader("Date", DateUtils.formatDate(new Date()));
         resp1.setHeader("Cache-Control", "public, max-age=3600");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(req1);
-        verifyMocks();
         Assert.assertNotNull(result.getFirstHeader("Via"));
     }
 
     @Test
     public void testSetsCacheHitContextIfRequestServedFromCache() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/");
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("ETag", "\"etag\"");
         resp1.setHeader("Date", DateUtils.formatDate(new Date()));
         resp1.setHeader("Cache-Control", "public, max-age=3600");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
         execute(req2);
-        verifyMocks();
         Assert.assertEquals(CacheResponseStatus.CACHE_HIT, context.getCacheResponseStatus());
     }
 
     @Test
     public void testSetsViaHeaderOnResponseIfRequestServedFromCache() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/");
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("ETag", "\"etag\"");
         resp1.setHeader("Date", DateUtils.formatDate(new Date()));
         resp1.setHeader("Cache-Control", "public, max-age=3600");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
         Assert.assertNotNull(result.getFirstHeader("Via"));
     }
 
@@ -506,12 +329,10 @@ public abstract class TestCachingExecChain {
     public void testReturns304ForIfModifiedSinceHeaderIfRequestServedFromCache() throws Exception {
         final Date now = new Date();
         final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/");
         req2.addHeader("If-Modified-Since", DateUtils.formatDate(now));
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("ETag", "\"etag\"");
@@ -519,14 +340,11 @@ public abstract class TestCachingExecChain {
         resp1.setHeader("Cache-Control", "public, max-age=3600");
         resp1.setHeader("Last-Modified", DateUtils.formatDate(tenSecondsAgo));
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
         Assert.assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode());
-
     }
 
     @Test
@@ -534,7 +352,6 @@ public abstract class TestCachingExecChain {
         final Date now = new Date();
         final Date oneHourAgo = new Date(now.getTime() - 3600 * 1000L);
         final Date inTenMinutes = new Date(now.getTime() + 600 * 1000L);
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         req1.addHeader("If-Modified-Since", DateUtils.formatDate(oneHourAgo));
         final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/");
@@ -545,31 +362,25 @@ public abstract class TestCachingExecChain {
         resp1.setHeader("Cache-control", "max-age=600");
         resp1.setHeader("Expires", DateUtils.formatDate(inTenMinutes));
 
-        expect(
-            mockExecChain.proceed(isA(ClassicHttpRequest.class), isA(ExecChain.Scope.class))).andReturn(resp1).once();
-
-        expect(
-            mockExecChain.proceed(isA(ClassicHttpRequest.class), isA(ExecChain.Scope.class))).andThrow(
-            new AssertionFailedError("Should have reused cached 304 response")).anyTimes();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
         Assert.assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode());
         Assert.assertFalse(result.containsHeader("Last-Modified"));
+
+        Mockito.verify(mockExecChain).proceed(Mockito.any(), Mockito.any());
     }
 
     @Test
     public void testReturns200ForIfModifiedSinceDateIsLess() throws Exception {
         final Date now = new Date();
         final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/");
 
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("ETag", "\"etag\"");
@@ -582,27 +393,24 @@ public abstract class TestCachingExecChain {
 
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
         Assert.assertEquals(HttpStatus.SC_OK, result.getCode());
-
     }
 
     @Test
     public void testReturns200ForIfModifiedSinceDateIsInvalid() throws Exception {
         final Date now = new Date();
         final Date tenSecondsAfter = new Date(now.getTime() + 10 * 1000L);
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/");
 
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("ETag", "\"etag\"");
@@ -613,48 +421,41 @@ public abstract class TestCachingExecChain {
         // invalid date (date in the future)
         req2.addHeader("If-Modified-Since", DateUtils.formatDate(tenSecondsAfter));
 
-        backendExpectsAnyRequestAndReturn(resp1).times(2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
         Assert.assertEquals(HttpStatus.SC_OK, result.getCode());
 
+        Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any());
     }
 
     @Test
     public void testReturns304ForIfNoneMatchHeaderIfRequestServedFromCache() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/");
         req2.addHeader("If-None-Match", "*");
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("ETag", "\"etag\"");
         resp1.setHeader("Date", DateUtils.formatDate(new Date()));
         resp1.setHeader("Cache-Control", "public, max-age=3600");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
         Assert.assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode());
 
     }
 
     @Test
     public void testReturns200ForIfNoneMatchHeaderFails() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/");
 
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("ETag", "\"etag\"");
@@ -665,27 +466,24 @@ public abstract class TestCachingExecChain {
 
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
         Assert.assertEquals(200, result.getCode());
-
     }
 
     @Test
     public void testReturns304ForIfNoneMatchHeaderAndIfModifiedSinceIfRequestServedFromCache() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final Date now = new Date();
         final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/");
 
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("ETag", "\"etag\"");
@@ -696,27 +494,22 @@ public abstract class TestCachingExecChain {
         req2.addHeader("If-None-Match", "*");
         req2.addHeader("If-Modified-Since", DateUtils.formatDate(now));
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
         Assert.assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode());
-
     }
 
     @Test
     public void testReturns200ForIfNoneMatchHeaderFailsIfModifiedSinceIgnored() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final Date now = new Date();
         final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/");
         req2.addHeader("If-None-Match", "\"abc\"");
         req2.addHeader("If-Modified-Since", DateUtils.formatDate(now));
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("ETag", "\"etag\"");
@@ -724,21 +517,16 @@ public abstract class TestCachingExecChain {
         resp1.setHeader("Cache-Control", "public, max-age=3600");
         resp1.setHeader("Last-Modified", DateUtils.formatDate(tenSecondsAgo));
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
         Assert.assertEquals(200, result.getCode());
-
     }
 
     @Test
     public void testReturns200ForOptionsFollowedByGetIfAuthorizationHeaderAndSharedCache() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.custom()
-            .setSharedCache(true).build());
+        impl = new CachingExec(cache, null, CacheConfig.custom().setSharedCache(true).build());
         final Date now = new Date();
         final ClassicHttpRequest req1 = new HttpOptions("http://foo.example.com/");
         req1.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
@@ -750,8 +538,7 @@ public abstract class TestCachingExecChain {
         resp1.setHeader("Date", DateUtils.formatDate(now));
         resp1.setHeader("Cache-Control", "public, max-age=3600");
         resp1.setHeader("Last-Modified", DateUtils.formatDate(now));
-        final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("ETag", "\"get-etag\"");
@@ -759,13 +546,12 @@ public abstract class TestCachingExecChain {
         resp1.setHeader("Cache-Control", "public, max-age=3600");
         resp1.setHeader("Last-Modified", DateUtils.formatDate(now));
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp2);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
         Assert.assertEquals(200, result.getCode());
     }
 
@@ -774,33 +560,29 @@ public abstract class TestCachingExecChain {
         final Date now = new Date();
         final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
 
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/");
 
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("ETag", "\"etag\"");
         resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
         resp1.setHeader("Cache-Control", "public, max-age=5");
 
-        final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp2.setEntity(HttpTestUtils.makeBody(128));
         resp2.setHeader("Content-Length", "128");
         resp2.setHeader("ETag", "\"etag\"");
         resp2.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
         resp2.setHeader("Cache-Control", "public, max-age=5");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp2);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         execute(req2);
-        verifyMocks();
         Assert.assertEquals(CacheResponseStatus.VALIDATED, context.getCacheResponseStatus());
     }
 
@@ -809,33 +591,30 @@ public abstract class TestCachingExecChain {
         final Date now = new Date();
         final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
 
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/");
 
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("ETag", "\"etag\"");
         resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
         resp1.setHeader("Cache-Control", "public, max-age=5");
 
-        final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp2.setEntity(HttpTestUtils.makeBody(128));
         resp2.setHeader("Content-Length", "128");
         resp2.setHeader("ETag", "\"etag\"");
         resp2.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
         resp2.setHeader("Cache-Control", "public, max-age=5");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
         Assert.assertNotNull(result.getFirstHeader("Via"));
     }
 
@@ -844,25 +623,23 @@ public abstract class TestCachingExecChain {
         final Date now = new Date();
         final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
 
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/");
 
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("ETag", "\"etag\"");
         resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
         resp1.setHeader("Cache-Control", "public, max-age=5, must-revalidate");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndThrows(new IOException());
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenThrow(new IOException());
+
         execute(req2);
-        verifyMocks();
         Assert.assertEquals(CacheResponseStatus.CACHE_MODULE_RESPONSE,
             context.getCacheResponseStatus());
     }
@@ -872,25 +649,23 @@ public abstract class TestCachingExecChain {
         final Date now = new Date();
         final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
 
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/");
 
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("ETag", "\"etag\"");
         resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
         resp1.setHeader("Cache-Control", "public, max-age=5");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndThrows(new IOException());
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenThrow(new IOException());
+
         execute(req2);
-        verifyMocks();
         Assert.assertEquals(CacheResponseStatus.CACHE_HIT, context.getCacheResponseStatus());
     }
 
@@ -899,25 +674,23 @@ public abstract class TestCachingExecChain {
         final Date now = new Date();
         final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
 
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/");
 
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("ETag", "\"etag\"");
         resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
         resp1.setHeader("Cache-Control", "public, max-age=5");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndThrows(new IOException());
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenThrow(new IOException());
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
         Assert.assertNotNull(result.getFirstHeader("Via"));
     }
 
@@ -927,12 +700,10 @@ public abstract class TestCachingExecChain {
         final Date now = new Date();
         final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
 
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/");
 
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("ETag", "\"etag\"");
@@ -945,12 +716,12 @@ public abstract class TestCachingExecChain {
         resp2.setHeader("Date", DateUtils.formatDate(now));
         resp2.setHeader("Cache-Control", "public, max-age=5");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp2);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         Assert.assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode());
     }
@@ -961,12 +732,10 @@ public abstract class TestCachingExecChain {
         final Date now = new Date();
         final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
 
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/");
 
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("ETag", "\"etag\"");
@@ -974,37 +743,32 @@ public abstract class TestCachingExecChain {
         resp1.setHeader("Cache-Control", "public, max-age=5");
 
         req2.addHeader("If-None-Match", "\"etag\"");
-        final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp2.setEntity(HttpTestUtils.makeBody(128));
         resp2.setHeader("Content-Length", "128");
         resp2.setHeader("ETag", "\"newetag\"");
         resp2.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
         resp2.setHeader("Cache-Control", "public, max-age=5");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp2);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         Assert.assertEquals(HttpStatus.SC_OK, result.getCode());
     }
 
     @Test
     public void testReturns304ForIfModifiedSincePassesIfRequestServedFromOrigin() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
-
         final Date now = new Date();
         final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
 
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/");
 
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("ETag", "\"etag\"");
@@ -1019,28 +783,26 @@ public abstract class TestCachingExecChain {
         resp1.setHeader("Last-Modified", DateUtils.formatDate(tenSecondsAgo));
         resp2.setHeader("Cache-Control", "public, max-age=5");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         Assert.assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode());
     }
 
     @Test
     public void testReturns200ForIfModifiedSinceFailsIfRequestServedFromOrigin() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final Date now = new Date();
         final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
 
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/");
 
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("ETag", "\"etag\"");
@@ -1049,8 +811,7 @@ public abstract class TestCachingExecChain {
         resp1.setHeader("Cache-Control", "public, max-age=5");
 
         req2.addHeader("If-Modified-Since", DateUtils.formatDate(tenSecondsAgo));
-        final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp2.setEntity(HttpTestUtils.makeBody(128));
         resp2.setHeader("Content-Length", "128");
         resp2.setHeader("ETag", "\"newetag\"");
@@ -1058,27 +819,25 @@ public abstract class TestCachingExecChain {
         resp1.setHeader("Last-Modified", DateUtils.formatDate(now));
         resp2.setHeader("Cache-Control", "public, max-age=5");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         Assert.assertEquals(HttpStatus.SC_OK, result.getCode());
     }
 
     @Test
     public void testVariantMissServerIfReturns304CacheReturns200() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final Date now = new Date();
 
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com");
         req1.addHeader("Accept-Encoding", "gzip");
 
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("Etag", "\"gzip_etag\"");
@@ -1093,8 +852,7 @@ public abstract class TestCachingExecChain {
         req2Server.addHeader("Accept-Encoding", "deflate");
         req2Server.addHeader("If-None-Match", "\"gzip_etag\"");
 
-        final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp2.setEntity(HttpTestUtils.makeBody(128));
         resp2.setHeader("Content-Length", "128");
         resp2.setHeader("Etag", "\"deflate_etag\"");
@@ -1109,8 +867,7 @@ public abstract class TestCachingExecChain {
         req3Server.addHeader("Accept-Encoding", "gzip,deflate");
         req3Server.addHeader("If-None-Match", "\"gzip_etag\",\"deflate_etag\"");
 
-        final ClassicHttpResponse resp3 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp3 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp3.setEntity(HttpTestUtils.makeBody(128));
         resp3.setHeader("Content-Length", "128");
         resp3.setHeader("Etag", "\"gzip_etag\"");
@@ -1118,18 +875,18 @@ public abstract class TestCachingExecChain {
         resp3.setHeader("Vary", "Accept-Encoding");
         resp3.setHeader("Cache-Control", "public, max-age=3600");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp2);
-        backendExpectsAnyRequestAndReturn(resp3);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         final ClassicHttpResponse result1 = execute(req1);
 
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result2 = execute(req2);
 
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
+
         final ClassicHttpResponse result3 = execute(req3);
 
-        verifyMocks();
         Assert.assertEquals(HttpStatus.SC_OK, result1.getCode());
         Assert.assertEquals(HttpStatus.SC_OK, result2.getCode());
         Assert.assertEquals(HttpStatus.SC_OK, result3.getCode());
@@ -1137,14 +894,12 @@ public abstract class TestCachingExecChain {
 
     @Test
     public void testVariantsMissServerReturns304CacheReturns304() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final Date now = new Date();
 
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com");
         req1.addHeader("Accept-Encoding", "gzip");
 
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(HttpTestUtils.makeBody(128));
         resp1.setHeader("Content-Length", "128");
         resp1.setHeader("Etag", "\"gzip_etag\"");
@@ -1159,8 +914,7 @@ public abstract class TestCachingExecChain {
         req2Server.addHeader("Accept-Encoding", "deflate");
         req2Server.addHeader("If-None-Match", "\"gzip_etag\"");
 
-        final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp2.setEntity(HttpTestUtils.makeBody(128));
         resp2.setHeader("Content-Length", "128");
         resp2.setHeader("Etag", "\"deflate_etag\"");
@@ -1182,17 +936,17 @@ public abstract class TestCachingExecChain {
         resp4.setHeader("Vary", "Accept-Encoding");
         resp4.setHeader("Cache-Control", "public, max-age=3600");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp2);
-        backendExpectsAnyRequestAndReturn(resp4);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         final ClassicHttpResponse result1 = execute(req1);
 
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result2 = execute(req2);
 
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp4);
+
         final ClassicHttpResponse result4 = execute(req4);
-        verifyMocks();
         Assert.assertEquals(HttpStatus.SC_OK, result1.getCode());
         Assert.assertEquals(HttpStatus.SC_OK, result2.getCode());
         Assert.assertEquals(HttpStatus.SC_NOT_MODIFIED, result4.getCode());
@@ -1201,13 +955,11 @@ public abstract class TestCachingExecChain {
 
     @Test
     public void testSocketTimeoutExceptionIsNotSilentlyCatched() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final Date now = new Date();
 
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com");
 
-        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK,
-            "OK");
+        final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
         resp1.setEntity(new InputStreamEntity(new InputStream() {
             private boolean closed;
 
@@ -1226,17 +978,12 @@ public abstract class TestCachingExecChain {
         }, 128, null));
         resp1.setHeader("Date", DateUtils.formatDate(now));
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
-        try {
+        Assert.assertThrows(SocketTimeoutException.class, () -> {
             final ClassicHttpResponse result1 = execute(req1);
             EntityUtils.toString(result1.getEntity());
-            Assert.fail("We should have had a SocketTimeoutException");
-        } catch (final SocketTimeoutException e) {
-        }
-        verifyMocks();
-
+        });
     }
 
     @Test
@@ -1246,12 +993,6 @@ public abstract class TestCachingExecChain {
 
     @Test
     public void testTooLargeResponsesAreNotCached() throws Exception {
-        mockCache = EasyMock.createStrictMock(HttpCache.class);
-        impl = createCachingExecChain(mockCache, mockValidityPolicy,
-                mockResponsePolicy, mockResponseGenerator, mockRequestPolicy, mockSuitabilityChecker,
-                mockResponseProtocolCompliance, mockRequestProtocolCompliance,
-                mockCacheRevalidator, mockConditionalRequestBuilder, config);
-
         final HttpHost host = new HttpHost("foo.example.com");
         final ClassicHttpRequest request = new HttpGet("http://foo.example.com/bar");
 
@@ -1266,15 +1007,18 @@ public abstract class TestCachingExecChain {
         originResponse.setHeader("Date", DateUtils.formatDate(responseGenerated));
         originResponse.setHeader("ETag", "\"etag\"");
 
-        replayMocks();
-        final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, mockEndpoint, context);
+        final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, mockExecRuntime, context);
         impl.cacheAndReturnResponse(host, request, originResponse, scope, requestSent, responseReceived);
 
-        verifyMocks();
+        Mockito.verify(cache, Mockito.never()).createCacheEntry(
+                Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
     }
 
     @Test
     public void testSmallEnoughResponsesAreCached() throws Exception {
+        final HttpCache mockCache = Mockito.mock(HttpCache.class);
+        impl = new CachingExec(mockCache, null, CacheConfig.DEFAULT);
+
         final HttpHost host = new HttpHost("foo.example.com");
         final ClassicHttpRequest request = new HttpGet("http://foo.example.com/bar");
 
@@ -1292,28 +1036,28 @@ public abstract class TestCachingExecChain {
         final HttpCacheEntry httpCacheEntry = HttpTestUtils.makeCacheEntry();
         final SimpleHttpResponse response = SimpleHttpResponse.create(HttpStatus.SC_OK);
 
-        EasyMock.expect(mockCache.createCacheEntry(
-                eq(host),
-                same(request),
-                same(originResponse),
-                isA(ByteArrayBuffer.class),
-                eq(requestSent),
-                eq(responseReceived))).andReturn(httpCacheEntry).once();
-        EasyMock.expect(mockResponseGenerator.generateResponse(
-                same(request),
-                same(httpCacheEntry))).andReturn(response).once();
-        replayMocks();
-
-        final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, mockEndpoint, context);
+        Mockito.when(mockCache.createCacheEntry(
+                Mockito.eq(host),
+                RequestEquivalent.eq(request),
+                ResponseEquivalent.eq(response),
+                Mockito.any(),
+                Mockito.eq(requestSent),
+                Mockito.eq(responseReceived))).thenReturn(httpCacheEntry);
+
+        final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, mockExecRuntime, context);
         impl.cacheAndReturnResponse(host, request, originResponse, scope, requestSent, responseReceived);
 
-        verifyMocks();
+        Mockito.verify(mockCache).createCacheEntry(
+                Mockito.any(),
+                Mockito.any(),
+                Mockito.any(),
+                Mockito.any(),
+                Mockito.any(),
+                Mockito.any());
     }
 
     @Test
     public void testIfOnlyIfCachedAndNoCacheEntryBackendNotCalled() throws Exception {
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
-
         request.addHeader("Cache-Control", "only-if-cached");
 
         final ClassicHttpResponse resp = execute(request);
@@ -1322,89 +1066,26 @@ public abstract class TestCachingExecChain {
     }
 
     @Test
-    public void testIfOnlyIfCachedAndEntryNotSuitableBackendNotCalled() throws Exception {
-
-        request.setHeader("Cache-Control", "only-if-cached");
-
-        entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Cache-Control",
-            "must-revalidate") });
-
-        requestIsFatallyNonCompliant(null);
-        requestPolicyAllowsCaching(true);
-        getCacheEntryReturns(entry);
-        cacheEntrySuitable(false);
-
-        replayMocks();
-        final ClassicHttpResponse resp = execute(request);
-        verifyMocks();
-
-        Assert.assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT, resp.getCode());
-    }
-
-    @Test
-    public void testIfOnlyIfCachedAndEntryExistsAndIsSuitableReturnsEntry() throws Exception {
-
-        request.setHeader("Cache-Control", "only-if-cached");
-
-        requestIsFatallyNonCompliant(null);
-        requestPolicyAllowsCaching(true);
-        getCacheEntryReturns(entry);
-        cacheEntrySuitable(true);
-        responseIsGeneratedFromCache(SimpleHttpResponse.create(HttpStatus.SC_OK));
-        entryHasStaleness(TimeValue.ZERO_MILLISECONDS);
-
-        replayMocks();
-        final ClassicHttpResponse resp = execute(request);
-        verifyMocks();
-    }
-
-    @Test
-    public void testDoesNotSetConnectionInContextOnCacheHit() throws Exception {
-        final DummyBackend backend = new DummyBackend();
-        final ClassicHttpResponse response = HttpTestUtils.make200Response();
-        response.setHeader("Cache-Control", "max-age=3600");
-        backend.setResponse(response);
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
-        final HttpClientContext ctx = HttpClientContext.create();
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockEndpoint, context), backend);
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockEndpoint, ctx), backend);
-    }
-
-    @Test
-    public void testSetsTargetHostInContextOnCacheHit() throws Exception {
-        final DummyBackend backend = new DummyBackend();
-        final ClassicHttpResponse response = HttpTestUtils.make200Response();
-        response.setHeader("Cache-Control", "max-age=3600");
-        backend.setResponse(response);
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
-        final HttpClientContext ctx = HttpClientContext.create();
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockEndpoint, context), backend);
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockEndpoint, ctx), backend);
-    }
-
-    @Test
     public void testSetsRouteInContextOnCacheHit() throws Exception {
-        final DummyBackend backend = new DummyBackend();
         final ClassicHttpResponse response = HttpTestUtils.make200Response();
         response.setHeader("Cache-Control", "max-age=3600");
-        backend.setResponse(response);
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(request), Mockito.any())).thenReturn(response);
+
         final HttpClientContext ctx = HttpClientContext.create();
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockEndpoint, context), backend);
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockEndpoint, ctx), backend);
-        assertEquals(route, ctx.getHttpRoute());
+        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain);
+        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, ctx), mockExecChain);
+        Assert.assertEquals(route, ctx.getHttpRoute());
     }
 
     @Test
     public void testSetsRequestInContextOnCacheHit() throws Exception {
-        final DummyBackend backend = new DummyBackend();
         final ClassicHttpResponse response = HttpTestUtils.make200Response();
         response.setHeader("Cache-Control", "max-age=3600");
-        backend.setResponse(response);
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(request), Mockito.any())).thenReturn(response);
+
         final HttpClientContext ctx = HttpClientContext.create();
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockEndpoint, context), backend);
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockEndpoint, ctx), backend);
+        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain);
+        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, ctx), mockExecChain);
         if (!HttpTestUtils.equivalent(request, ctx.getRequest())) {
             assertSame(request, ctx.getRequest());
         }
@@ -1412,14 +1093,13 @@ public abstract class TestCachingExecChain {
 
     @Test
     public void testSetsResponseInContextOnCacheHit() throws Exception {
-        final DummyBackend backend = new DummyBackend();
         final ClassicHttpResponse response = HttpTestUtils.make200Response();
         response.setHeader("Cache-Control", "max-age=3600");
-        backend.setResponse(response);
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(request), Mockito.any())).thenReturn(response);
+
         final HttpClientContext ctx = HttpClientContext.create();
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockEndpoint, context), backend);
-        final ClassicHttpResponse result = impl.execute(request, new ExecChain.Scope("test", route, request, mockEndpoint, ctx), null);
+        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain);
+        final ClassicHttpResponse result = impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, ctx), null);
         if (!HttpTestUtils.equivalent(result, ctx.getResponse())) {
             assertSame(result, ctx.getResponse());
         }
@@ -1427,14 +1107,13 @@ public abstract class TestCachingExecChain {
 
     @Test
     public void testSetsRequestSentInContextOnCacheHit() throws Exception {
-        final DummyBackend backend = new DummyBackend();
         final ClassicHttpResponse response = HttpTestUtils.make200Response();
         response.setHeader("Cache-Control", "max-age=3600");
-        backend.setResponse(response);
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(request), Mockito.any())).thenReturn(response);
+
         final HttpClientContext ctx = HttpClientContext.create();
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockEndpoint, context), backend);
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockEndpoint, ctx), backend);
+        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain);
+        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, ctx), mockExecChain);
     }
 
     @Test
@@ -1442,12 +1121,12 @@ public abstract class TestCachingExecChain {
         final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content");
         response.setHeader("Date", DateUtils.formatDate(new Date()));
         response.setHeader("Cache-Control", "max-age=300");
-        final DummyBackend backend = new DummyBackend();
-        backend.setResponse(response);
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockEndpoint, context), backend);
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockEndpoint, context), backend);
-        assertEquals(1, backend.getExecutions());
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(request), Mockito.any())).thenReturn(response);
+
+        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain);
+        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain);
+
+        Mockito.verify(mockExecChain).proceed(Mockito.any(), Mockito.any());
     }
 
     @Test
@@ -1456,7 +1135,6 @@ public abstract class TestCachingExecChain {
         final Date now = new Date();
         final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
 
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         req1.addHeader("If-None-Match", "\"etag\"");
 
@@ -1466,13 +1144,11 @@ public abstract class TestCachingExecChain {
         resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
         resp1.setHeader("Cache-Control", "public, max-age=5");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
         final ClassicHttpResponse result = execute(req1);
-        verifyMocks();
 
-        assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode());
-        assertNull("The 304 response messages MUST NOT contain a message-body", result.getEntity());
+        Assert.assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode());
+        Assert.assertNull("The 304 response messages MUST NOT contain a message-body", result.getEntity());
     }
 
     @Test
@@ -1480,8 +1156,6 @@ public abstract class TestCachingExecChain {
 
         final Date now = new Date();
 
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
-
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         req1.addHeader("If-None-Match", "etag");
 
@@ -1498,17 +1172,17 @@ public abstract class TestCachingExecChain {
         resp2.setHeader("Cache-Control", "max-age=0");
         resp1.setHeader("Etag", "etag");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp2);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
+
         final ClassicHttpResponse result1 = execute(req1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result2 = execute(req2);
-        verifyMocks();
 
-        assertEquals(HttpStatus.SC_NOT_MODIFIED, result1.getCode());
-        assertEquals("etag", result1.getFirstHeader("Etag").getValue());
-        assertEquals(HttpStatus.SC_NOT_MODIFIED, result2.getCode());
-        assertEquals("etag", result2.getFirstHeader("Etag").getValue());
+        Assert.assertEquals(HttpStatus.SC_NOT_MODIFIED, result1.getCode());
+        Assert.assertEquals("etag", result1.getFirstHeader("Etag").getValue());
+        Assert.assertEquals(HttpStatus.SC_NOT_MODIFIED, result2.getCode());
+        Assert.assertEquals("etag", result2.getFirstHeader("Etag").getValue());
     }
 
     @Test
@@ -1516,8 +1190,6 @@ public abstract class TestCachingExecChain {
 
         final Date now = new Date();
 
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
-
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         req1.addHeader("If-None-Match", "etag");
 
@@ -1536,17 +1208,18 @@ public abstract class TestCachingExecChain {
         resp1.setHeader("Etag", "etag");
         resp1.setHeader("Vary", "Accept-Encoding");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp2);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
+
         final ClassicHttpResponse result1 = execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result2 = execute(req2);
-        verifyMocks();
 
-        assertEquals(HttpStatus.SC_NOT_MODIFIED, result1.getCode());
-        assertEquals("etag", result1.getFirstHeader("Etag").getValue());
-        assertEquals(HttpStatus.SC_NOT_MODIFIED, result2.getCode());
-        assertEquals("etag", result2.getFirstHeader("Etag").getValue());
+        Assert.assertEquals(HttpStatus.SC_NOT_MODIFIED, result1.getCode());
+        Assert.assertEquals("etag", result1.getFirstHeader("Etag").getValue());
+        Assert.assertEquals(HttpStatus.SC_NOT_MODIFIED, result2.getCode());
+        Assert.assertEquals("etag", result2.getFirstHeader("Etag").getValue());
     }
 
     @Test
@@ -1555,8 +1228,6 @@ public abstract class TestCachingExecChain {
         final Date now = new Date();
         final Date inOneMinute = new Date(System.currentTimeMillis() + 60000);
 
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
-
         final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/");
         req1.addHeader("If-None-Match", "etag");
 
@@ -1578,122 +1249,38 @@ public abstract class TestCachingExecChain {
         resp2.setHeader("Vary", "Accept-Encoding");
         resp2.setEntity(HttpTestUtils.makeBody(128));
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp2).anyTimes();
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
+
         final ClassicHttpResponse result1 = execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result2 = execute(req2);
-        verifyMocks();
 
-        assertEquals(HttpStatus.SC_NOT_MODIFIED, result1.getCode());
-        assertNull(result1.getEntity());
-        assertEquals(HttpStatus.SC_OK, result2.getCode());
+        Assert.assertEquals(HttpStatus.SC_NOT_MODIFIED, result1.getCode());
+        Assert.assertNull(result1.getEntity());
+        Assert.assertEquals(HttpStatus.SC_OK, result2.getCode());
         Assert.assertNotNull(result2.getEntity());
     }
 
     @Test
     public void testUsesVirtualHostForCacheKey() throws Exception {
-        final DummyBackend backend = new DummyBackend();
         final ClassicHttpResponse response = HttpTestUtils.make200Response();
         response.setHeader("Cache-Control", "max-age=3600");
-        backend.setResponse(response);
-        impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockEndpoint, context), backend);
-        assertEquals(1, backend.getExecutions());
-        request.setAuthority(new URIAuthority("bar.example.com"));
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockEndpoint, context), backend);
-        assertEquals(2, backend.getExecutions());
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockEndpoint, context), backend);
-        assertEquals(2, backend.getExecutions());
-    }
-
-    protected IExpectationSetters<ClassicHttpResponse> backendExpectsRequestAndReturn(
-            final ClassicHttpRequest request, final ClassicHttpResponse response) throws Exception {
-        final ClassicHttpResponse resp = mockExecChain.proceed(
-                EasyMock.eq(request), EasyMock.isA(ExecChain.Scope.class));
-        return EasyMock.expect(resp).andReturn(response);
-    }
-
-    private IExpectationSetters<ClassicHttpResponse> backendExpectsAnyRequestAndReturn(
-        final ClassicHttpResponse response) throws Exception {
-        final ClassicHttpResponse resp = mockExecChain.proceed(
-            EasyMock.isA(ClassicHttpRequest.class), EasyMock.isA(ExecChain.Scope.class));
-        return EasyMock.expect(resp).andReturn(response);
-    }
-
-    protected IExpectationSetters<ClassicHttpResponse> backendExpectsAnyRequestAndThrows(
-        final Throwable throwable) throws Exception {
-        final ClassicHttpResponse resp = mockExecChain.proceed(
-            EasyMock.isA(ClassicHttpRequest.class), EasyMock.isA(ExecChain.Scope.class));
-        return EasyMock.expect(resp).andThrow(throwable);
-    }
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(response);
 
-    protected IExpectationSetters<ClassicHttpResponse> backendCaptureRequestAndReturn(
-            final Capture<ClassicHttpRequest> cap, final ClassicHttpResponse response) throws Exception {
-        final ClassicHttpResponse resp = mockExecChain.proceed(
-            EasyMock.capture(cap), EasyMock.isA(ExecChain.Scope.class));
-        return EasyMock.expect(resp).andReturn(response);
-    }
-
-    protected void getCacheEntryReturns(final HttpCacheEntry result) throws IOException {
-        expect(mockCache.getCacheEntry(eq(host), eqRequest(request))).andReturn(result);
-    }
-
-    private void cacheInvalidatorWasCalled() throws IOException {
-        mockCache.flushCacheEntriesInvalidatedByRequest((HttpHost) anyObject(), (HttpRequest) anyObject());
-    }
-
-    protected void cacheEntryValidatable(final boolean b) {
-        expect(mockValidityPolicy.isRevalidatable((HttpCacheEntry) anyObject())).andReturn(b)
-            .anyTimes();
-    }
-
-    protected void cacheEntryMustRevalidate(final boolean b) {
-        expect(mockValidityPolicy.mustRevalidate(mockCacheEntry)).andReturn(b);
-    }
-
-    protected void cacheEntryProxyRevalidate(final boolean b) {
-        expect(mockValidityPolicy.proxyRevalidate(mockCacheEntry)).andReturn(b);
-    }
-
-    protected void mayReturnStaleWhileRevalidating(final boolean b) {
-        expect(
-            mockValidityPolicy.mayReturnStaleWhileRevalidating((HttpCacheEntry) anyObject(),
-                (Date) anyObject())).andReturn(b);
-    }
-
-    protected void conditionalRequestBuilderReturns(final ClassicHttpRequest validate) throws Exception {
-        expect(mockConditionalRequestBuilder.buildConditionalRequest(request, entry)).andReturn(
-            validate);
-    }
+        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain);
 
-    protected void requestPolicyAllowsCaching(final boolean allow) {
-        expect(mockRequestPolicy.isServableFromCache((HttpRequest) anyObject())).andReturn(allow);
-    }
+        Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any());
 
-    protected void cacheEntrySuitable(final boolean suitable) {
-        expect(
-            mockSuitabilityChecker.canCachedResponseBeUsed((HttpHost) anyObject(),
-                (HttpRequest) anyObject(), (HttpCacheEntry) anyObject(), (Date) anyObject()))
-            .andReturn(suitable);
-    }
+        request.setAuthority(new URIAuthority("bar.example.com"));
+        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain);
 
-    private void entryHasStaleness(final TimeValue staleness) {
-        expect(
-            mockValidityPolicy.getStaleness((HttpCacheEntry) anyObject(), (Date) anyObject()))
-            .andReturn(staleness);
-    }
+        Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any());
 
-    protected void responseIsGeneratedFromCache(final SimpleHttpResponse cachedResponse) throws IOException {
-        expect(
-            mockResponseGenerator.generateResponse(
-                    (ClassicHttpRequest) anyObject(),
-                    (HttpCacheEntry) anyObject())).andReturn(cachedResponse);
-    }
+        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain);
 
-    protected void doesNotFlushCache() throws IOException {
-        mockCache.flushCacheEntriesInvalidatedByRequest(isA(HttpHost.class), isA(HttpRequest.class));
-        EasyMock.expectLastCall().andThrow(new AssertionError("flushCacheEntriesInvalidByResponse should not have been called")).anyTimes();
+        Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any());
     }
 
 }
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestProtocolAllowedBehavior.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestProtocolAllowedBehavior.java
index e285965..a4146f8 100644
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestProtocolAllowedBehavior.java
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestProtocolAllowedBehavior.java
@@ -26,26 +26,90 @@
  */
 package org.apache.hc.client5.http.impl.cache;
 
+import java.io.IOException;
 import java.net.SocketTimeoutException;
 import java.util.Date;
 
+import org.apache.hc.client5.http.HttpRoute;
+import org.apache.hc.client5.http.classic.ExecChain;
+import org.apache.hc.client5.http.classic.ExecRuntime;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
 import org.apache.hc.client5.http.utils.DateUtils;
 import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.HttpResponse;
 import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
 import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
 
 /**
  * This class tests behavior that is allowed (MAY) by the HTTP/1.1 protocol
  * specification and for which we have implemented the behavior in HTTP cache.
  */
-public class TestProtocolAllowedBehavior extends AbstractProtocolTest {
+@RunWith(MockitoJUnitRunner.class)
+public class TestProtocolAllowedBehavior {
+
+    static final int MAX_BYTES = 1024;
+    static final int MAX_ENTRIES = 100;
+    static final int ENTITY_LENGTH = 128;
+
+    HttpHost host;
+    HttpRoute route;
+    HttpClientContext context;
+    @Mock
+    ExecChain mockExecChain;
+    @Mock
+    ExecRuntime mockExecRuntime;
+    @Mock
+    HttpCache mockCache;
+    ClassicHttpRequest request;
+    ClassicHttpResponse originResponse;
+    CacheConfig config;
+    CachingExec impl;
+    HttpCache cache;
+
+    @Before
+    public void setUp() throws Exception {
+        host = new HttpHost("foo.example.com", 80);
+
+        route = new HttpRoute(host);
+
+        request = new BasicClassicHttpRequest("GET", "/foo");
+
+        context = HttpClientContext.create();
+
+        originResponse = HttpTestUtils.make200Response();
+
+        config = CacheConfig.custom()
+                .setMaxCacheEntries(MAX_ENTRIES)
+                .setMaxObjectSize(MAX_BYTES)
+                .setSharedCache(false)
+                .build();
+
+        cache = new BasicHttpCache(config);
+        impl = new CachingExec(cache, null, config);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
+    }
+
+    public ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException {
+        return impl.execute(
+                ClassicRequestBuilder.copy(request).build(),
+                new ExecChain.Scope("test", route, request, mockExecRuntime, context),
+                mockExecChain);
+    }
 
     @Test
-    public void testNonSharedCacheReturnsStaleResponseWhenRevalidationFailsForProxyRevalidate()
-        throws Exception {
+    public void testNonSharedCacheReturnsStaleResponseWhenRevalidationFailsForProxyRevalidate() throws Exception {
         final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET","/");
         final Date now = new Date();
         final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
@@ -53,37 +117,33 @@ public class TestProtocolAllowedBehavior extends AbstractProtocolTest {
         originResponse.setHeader("Cache-Control","max-age=5,proxy-revalidate");
         originResponse.setHeader("Etag","\"etag\"");
 
-        backendExpectsAnyRequest().andReturn(originResponse);
-
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET","/");
 
-        backendExpectsAnyRequest().andThrow(new SocketTimeoutException());
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
-        behaveAsNonSharedCache();
         execute(req1);
-        final HttpResponse result = execute(req2);
-        verifyMocks();
 
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenThrow(new SocketTimeoutException());
+
+        final HttpResponse result = execute(req2);
         Assert.assertEquals(HttpStatus.SC_OK, result.getCode());
+
+        Mockito.verifyNoInteractions(mockCache);
     }
 
     @Test
-    public void testNonSharedCacheMayCacheResponsesWithCacheControlPrivate()
-        throws Exception {
+    public void testNonSharedCacheMayCacheResponsesWithCacheControlPrivate() throws Exception {
         final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET","/");
         originResponse.setHeader("Cache-Control","private,max-age=3600");
 
-        backendExpectsAnyRequest().andReturn(originResponse);
-
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET","/");
 
-        replayMocks();
-        behaveAsNonSharedCache();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
+
         execute(req1);
         final HttpResponse result = execute(req2);
-        verifyMocks();
-
         Assert.assertEquals(HttpStatus.SC_OK, result.getCode());
+
+        Mockito.verifyNoInteractions(mockCache);
     }
 }
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestProtocolDeviations.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestProtocolDeviations.java
index 0d16bc4..6b89f14 100644
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestProtocolDeviations.java
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestProtocolDeviations.java
@@ -42,18 +42,18 @@ import org.apache.hc.core5.http.ClassicHttpResponse;
 import org.apache.hc.core5.http.HttpEntity;
 import org.apache.hc.core5.http.HttpException;
 import org.apache.hc.core5.http.HttpHost;
-import org.apache.hc.core5.http.HttpRequest;
 import org.apache.hc.core5.http.HttpResponse;
 import org.apache.hc.core5.http.HttpStatus;
 import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
 import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
 import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
-import org.easymock.Capture;
-import org.easymock.EasyMock;
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
 
 /**
  * We are a conditionally-compliant HTTP/1.1 client with a cache. However, a lot
@@ -70,22 +70,27 @@ import org.junit.Test;
  * document the places where we differ from the HTTP RFC.
  */
 @SuppressWarnings("boxing") // test code
+@RunWith(MockitoJUnitRunner.class)
 public class TestProtocolDeviations {
 
     private static final int MAX_BYTES = 1024;
     private static final int MAX_ENTRIES = 100;
 
-    private HttpHost host;
-    private HttpRoute route;
-    private HttpEntity mockEntity;
-    private ExecRuntime mockEndpoint;
-    private ExecChain mockExecChain;
-    private HttpCache mockCache;
-    private ClassicHttpRequest request;
-    private HttpCacheContext context;
-    private ClassicHttpResponse originResponse;
-
-    private ExecChainHandler impl;
+    HttpHost host;
+    HttpRoute route;
+    @Mock
+    HttpEntity mockEntity;
+    @Mock
+    ExecRuntime mockEndpoint;
+    @Mock
+    ExecChain mockExecChain;
+    @Mock
+    HttpCache mockCache;
+    ClassicHttpRequest request;
+    HttpCacheContext context;
+    ClassicHttpResponse originResponse;
+
+    ExecChainHandler impl;
 
     @Before
     public void setUp() {
@@ -105,11 +110,6 @@ public class TestProtocolDeviations {
                 .build();
 
         final HttpCache cache = new BasicHttpCache(config);
-        mockEndpoint = EasyMock.createNiceMock(ExecRuntime.class);
-        mockExecChain = EasyMock.createNiceMock(ExecChain.class);
-        mockEntity = EasyMock.createNiceMock(HttpEntity.class);
-        mockCache = EasyMock.createNiceMock(HttpCache.class);
-
         impl = createCachingExecChain(cache, config);
     }
 
@@ -131,122 +131,12 @@ public class TestProtocolDeviations {
         return out;
     }
 
-    private void replayMocks() {
-        EasyMock.replay(mockExecChain);
-        EasyMock.replay(mockCache);
-        EasyMock.replay(mockEntity);
-    }
-
-    private void verifyMocks() {
-        EasyMock.verify(mockExecChain);
-        EasyMock.verify(mockCache);
-        EasyMock.verify(mockEntity);
-    }
-
     private HttpEntity makeBody(final int nbytes) {
         final byte[] bytes = new byte[nbytes];
         new Random().nextBytes(bytes);
         return new ByteArrayEntity(bytes, null);
     }
 
-    public static HttpRequest eqRequest(final HttpRequest in) {
-        org.easymock.EasyMock.reportMatcher(new RequestEquivalent(in));
-        return null;
-    }
-
-    /*
-     * "For compatibility with HTTP/1.0 applications, HTTP/1.1 requests
-     * containing a message-body MUST include a valid Content-Length header
-     * field unless the server is known to be HTTP/1.1 compliant. If a request
-     * contains a message-body and a Content-Length is not given, the server
-     * SHOULD respond with 400 (bad request) if it cannot determine the length
-     * of the message, or with 411 (length required) if it wishes to insist on
-     * receiving a valid Content-Length."
-     *
-     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4
-     *
-     * 8/23/2010 JRC - This test has been moved to Ignore.  The caching client
-     * was set to return status code 411 on a missing content-length header when
-     * a request had a body.  It seems that somewhere deeper in the client stack
-     * this header is added automatically for us - so the caching client shouldn't
-     * specifically be worried about this requirement.
-     */
-    @Ignore
-    public void testHTTP1_1RequestsWithBodiesOfKnownLengthMustHaveContentLength() throws Exception {
-        final ClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/");
-        post.setEntity(mockEntity);
-
-        replayMocks();
-
-        final HttpResponse response = execute(post);
-
-        verifyMocks();
-
-        Assert.assertEquals(HttpStatus.SC_LENGTH_REQUIRED, response.getCode());
-    }
-
-    /*
-     * Discussion: if an incoming request has a body, but the HttpEntity
-     * attached has an unknown length (meaning entity.getContentLength() is
-     * negative), we have two choices if we want to be conditionally compliant.
-     * (1) we can slurp the whole body into a bytearray and compute its length
-     * before sending; or (2) we can push responsibility for (1) back onto the
-     * client by just generating a 411 response
-     *
-     * There is a third option, which is that we delegate responsibility for (1)
-     * onto the backend HttpClient, but because that is an injected dependency,
-     * we can't rely on it necessarily being conditionally compliant with
-     * HTTP/1.1. Currently, option (2) seems like the safest bet, as this
-     * exposes to the client application that the slurping required for (1)
-     * needs to happen in order to compute the content length.
-     *
-     * In any event, this test just captures the behavior required.
-     *
-     * 8/23/2010 JRC - This test has been moved to Ignore.  The caching client
-     * was set to return status code 411 on a missing content-length header when
-     * a request had a body.  It seems that somewhere deeper in the client stack
-     * this header is added automatically for us - so the caching client shouldn't
-     * specifically be worried about this requirement.
-     */
-    @Ignore
-    public void testHTTP1_1RequestsWithUnknownBodyLengthAreRejectedOrHaveContentLengthAdded()
-            throws Exception {
-        final ClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/");
-
-        final byte[] bytes = new byte[128];
-        new Random().nextBytes(bytes);
-
-        final HttpEntity mockBody = EasyMock.createMockBuilder(ByteArrayEntity.class).withConstructor(
-                new Object[] { bytes }).addMockedMethods("getContentLength").createNiceMock();
-        org.easymock.EasyMock.expect(mockBody.getContentLength()).andReturn(-1L).anyTimes();
-        post.setEntity(mockBody);
-
-        final Capture<ClassicHttpRequest> reqCap = EasyMock.newCapture();
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(reqCap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(
-                                originResponse).times(0, 1);
-
-        replayMocks();
-        EasyMock.replay(mockBody);
-
-        final HttpResponse result = execute(post);
-
-        verifyMocks();
-        EasyMock.verify(mockBody);
-
-        if (reqCap.hasCaptured()) {
-            // backend request was made
-            final HttpRequest forwarded = reqCap.getValue();
-            Assert.assertNotNull(forwarded.getFirstHeader("Content-Length"));
-        } else {
-            final int status = result.getCode();
-            Assert.assertTrue(HttpStatus.SC_LENGTH_REQUIRED == status
-                    || HttpStatus.SC_BAD_REQUEST == status);
-        }
-    }
-
     /*
      * "10.2.7 206 Partial Content ... The request MUST have included a Range
      * header field (section 14.35) indicating the desired range, and MAY have
@@ -266,12 +156,8 @@ public class TestProtocolDeviations {
         originResponse.setHeader("Content-Range", "bytes 0-499/1234");
         originResponse.setEntity(makeBody(500));
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         try {
             final HttpResponse result = execute(request);
             Assert.assertTrue(HttpStatus.SC_PARTIAL_CONTENT != result.getCode());
@@ -292,13 +178,9 @@ public class TestProtocolDeviations {
 
         originResponse = new BasicClassicHttpResponse(401, "Unauthorized");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
+
         final HttpResponse result = execute(request);
-        verifyMocks();
         Assert.assertSame(originResponse, result);
     }
 
@@ -312,13 +194,9 @@ public class TestProtocolDeviations {
     public void testPassesOnOrigin405WithoutAllowHeader() throws Exception {
         originResponse = new BasicClassicHttpResponse(405, "Method Not Allowed");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
+
         final HttpResponse result = execute(request);
-        verifyMocks();
         Assert.assertSame(originResponse, result);
     }
 
@@ -333,13 +211,9 @@ public class TestProtocolDeviations {
     public void testPassesOnOrigin407WithoutAProxyAuthenticateHeader() throws Exception {
         originResponse = new BasicClassicHttpResponse(407, "Proxy Authentication Required");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
+
         final HttpResponse result = execute(request);
-        verifyMocks();
         Assert.assertSame(originResponse, result);
     }
 
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestProtocolRecommendations.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestProtocolRecommendations.java
index ef25006..d82f845 100644
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestProtocolRecommendations.java
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestProtocolRecommendations.java
@@ -38,44 +38,101 @@ import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
 
+import org.apache.hc.client5.http.HttpRoute;
 import org.apache.hc.client5.http.auth.StandardAuthScheme;
 import org.apache.hc.client5.http.classic.ExecChain;
+import org.apache.hc.client5.http.classic.ExecRuntime;
 import org.apache.hc.client5.http.classic.methods.HttpGet;
 import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
 import org.apache.hc.client5.http.utils.DateUtils;
 import org.apache.hc.core5.http.ClassicHttpRequest;
 import org.apache.hc.core5.http.ClassicHttpResponse;
 import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HeaderElement;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpException;
 import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.HttpStatus;
 import org.apache.hc.core5.http.HttpVersion;
+import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
 import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
 import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
 import org.apache.hc.core5.http.message.MessageSupport;
-import org.easymock.Capture;
-import org.easymock.EasyMock;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
 
 /*
  * This test class captures functionality required to achieve unconditional
  * compliance with the HTTP/1.1 spec, i.e. all the SHOULD, SHOULD NOT,
  * RECOMMENDED, and NOT RECOMMENDED behaviors.
  */
-public class TestProtocolRecommendations extends AbstractProtocolTest {
+@RunWith(MockitoJUnitRunner.class)
+public class TestProtocolRecommendations {
+
+    static final int MAX_BYTES = 1024;
+    static final int MAX_ENTRIES = 100;
+    static final int ENTITY_LENGTH = 128;
+
+    HttpHost host;
+    HttpRoute route;
+    HttpEntity body;
+    HttpClientContext context;
+    @Mock
+    ExecChain mockExecChain;
+    @Mock
+    ExecRuntime mockExecRuntime;
+    @Mock
+    HttpCache mockCache;
+    ClassicHttpRequest request;
+    ClassicHttpResponse originResponse;
+    CacheConfig config;
+    CachingExec impl;
+    HttpCache cache;
+    Date now;
+    Date tenSecondsAgo;
+    Date twoMinutesAgo;
 
-    private Date now;
-    private Date tenSecondsAgo;
-    private Date twoMinutesAgo;
-
-    @Override
     @Before
-    public void setUp() {
-        super.setUp();
+    public void setUp() throws Exception {
+        host = new HttpHost("foo.example.com", 80);
+
+        route = new HttpRoute(host);
+
+        body = HttpTestUtils.makeBody(ENTITY_LENGTH);
+
+        request = new BasicClassicHttpRequest("GET", "/foo");
+
+        context = HttpClientContext.create();
+
+        originResponse = HttpTestUtils.make200Response();
+
+        config = CacheConfig.custom()
+                .setMaxCacheEntries(MAX_ENTRIES)
+                .setMaxObjectSize(MAX_BYTES)
+                .build();
+
+        cache = new BasicHttpCache(config);
+        impl = new CachingExec(cache, null, config);
+
         now = new Date();
         tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
         twoMinutesAgo = new Date(now.getTime() - 2 * 60 * 1000L);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
+    }
+
+    public ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException {
+        return impl.execute(
+                ClassicRequestBuilder.copy(request).build(),
+                new ExecChain.Scope("test", route, request, mockExecRuntime, context),
+                mockExecChain);
     }
 
     /* "identity: The default (identity) encoding; the use of no
@@ -88,10 +145,10 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
     @Test
     public void testIdentityCodingIsNotUsedInContentEncodingHeader() throws Exception {
         originResponse.setHeader("Content-Encoding", "identity");
-        backendExpectsAnyRequest().andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
+
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
+
         boolean foundIdentity = false;
         final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.CONTENT_ENCODING);
         while (it.hasNext()) {
@@ -119,15 +176,14 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader(validatorHeader, validator);
         resp1.setHeader(headerName, headerValue);
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader(conditionalHeader, validator);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
+
 
         if (HttpStatus.SC_NOT_MODIFIED == result.getCode()) {
             assertNull(result.getFirstHeader(headerName));
@@ -216,7 +272,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader(validatorHeader, validator);
         resp1.setHeader("Content-Range", "bytes 0-127/256");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("If-Range", validator);
@@ -229,19 +285,15 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
 
         // cache module does not currently deal with byte ranges, but we want
         // this test to work even if it does some day
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(cap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2).times(0,1);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
-        if (!cap.hasCaptured()
-            && HttpStatus.SC_NOT_MODIFIED == result.getCode()) {
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain, Mockito.atMost(2)).proceed(reqCapture.capture(), Mockito.any());
+
+        final List<ClassicHttpRequest> allRequests = reqCapture.getAllValues();
+        if (allRequests.isEmpty() && HttpStatus.SC_NOT_MODIFIED == result.getCode()) {
             // cache generated a 304
             assertNull(result.getFirstHeader("Content-Range"));
         }
@@ -293,11 +345,9 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp.setHeader("Etag", "\"etag\"");
         resp.setHeader(entityHeader, entityHeaderValue);
 
-        backendExpectsAnyRequestAndReturn(resp);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(req);
-        verifyMocks();
 
         assertNull(result.getFirstHeader(entityHeader));
     }
@@ -349,11 +399,9 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp.setHeader("ETag", "\"etag\"");
         resp.setHeader("Content-Range", "bytes 0-127/256");
 
-        backendExpectsAnyRequestAndReturn(resp);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(req);
-        verifyMocks();
 
         assertNull(result.getFirstHeader("Content-Range"));
     }
@@ -376,23 +424,22 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Cache-Control","public,max-age=5");
         resp1.setHeader("Etag","\"etag\"");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
         return req1;
     }
 
     private void testDoesNotReturnStaleResponseOnError(final ClassicHttpRequest req2) throws Exception {
         final ClassicHttpRequest req1 = requestToPopulateStaleCacheEntry();
 
-        backendExpectsAnyRequest().andThrow(new IOException());
-
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenThrow(new IOException());
+
         ClassicHttpResponse result = null;
         try {
             result = execute(req2);
         } catch (final IOException acceptable) {
         }
-        verifyMocks();
 
         if (result != null) {
             assertFalse(result.getCode() == HttpStatus.SC_OK);
@@ -450,15 +497,14 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("Cache-Control","max-stale=20");
 
-        backendExpectsAnyRequest().andThrow(new IOException()).times(0,1);
-
-        replayMocks();
         execute(req1);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         assertEquals(HttpStatus.SC_OK, result.getCode());
         assertNotNull(result.getFirstHeader("Warning"));
+
+        Mockito.verify(mockExecChain, Mockito.atMost(1)).proceed(Mockito.any(), Mockito.any());
     }
 
     /*
@@ -502,16 +548,17 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("ETag","\"etag\"");
         resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
 
-        backendExpectsAnyRequest().andThrow(new IOException()).anyTimes();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenThrow(new IOException());
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
+
+        Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any());
 
         assertEquals(HttpStatus.SC_OK, result.getCode());
         boolean warning111Found = false;
@@ -544,11 +591,9 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         originResponse.setHeader("Cache-Control","public, max-age=5");
         originResponse.setHeader("ETag","\"etag\"");
 
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
 
         assertNull(result.getFirstHeader("Warning"));
     }
@@ -562,11 +607,9 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         final String warning = "110 fred \"Response is stale\"";
         originResponse.addHeader("Warning",warning);
 
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
 
         assertEquals(warning, result.getFirstHeader("Warning").getValue());
     }
@@ -580,27 +623,23 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
     private void testDoesNotModifyHeaderOnResponses(final String headerName) throws Exception {
         final String headerValue = HttpTestUtils
             .getCanonicalHeaderValue(originResponse, headerName);
-        backendExpectsAnyRequest().andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
+
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
-        assertEquals(headerValue,
-            result.getFirstHeader(headerName).getValue());
+
+        assertEquals(headerValue, result.getFirstHeader(headerName).getValue());
     }
 
     private void testDoesNotModifyHeaderOnRequests(final String headerName) throws Exception {
         final String headerValue = HttpTestUtils.getCanonicalHeaderValue(request, headerName);
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(cap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
+
         execute(request);
-        verifyMocks();
-        assertEquals(headerValue,
-                HttpTestUtils.getCanonicalHeaderValue(cap.getValue(),
-                        headerName));
+
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any());
+
+        assertEquals(headerValue, HttpTestUtils.getCanonicalHeaderValue(reqCapture.getValue(), headerName));
     }
 
     @Test
@@ -833,25 +872,22 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Last-Modified", lmDate);
         resp1.setHeader("Cache-Control","max-age=5");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(cap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         execute(req2);
-        verifyMocks();
 
-        final ClassicHttpRequest captured = cap.getValue();
-        final Header ifModifiedSince =
-            captured.getFirstHeader("If-Modified-Since");
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain, Mockito.times(2)).proceed(reqCapture.capture(), Mockito.any());
+
+        final ClassicHttpRequest captured = reqCapture.getValue();
+        final Header ifModifiedSince = captured.getFirstHeader("If-Modified-Since");
         assertEquals(lmDate, ifModifiedSince.getValue());
     }
 
@@ -876,28 +912,24 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Cache-Control","max-age=5");
         resp1.setHeader("ETag", etag);
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(cap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         execute(req2);
-        verifyMocks();
 
-        final ClassicHttpRequest captured = cap.getValue();
-        final Header ifModifiedSince =
-            captured.getFirstHeader("If-Modified-Since");
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain, Mockito.times(2)).proceed(reqCapture.capture(), Mockito.any());
+
+        final ClassicHttpRequest captured = reqCapture.getValue();
+        final Header ifModifiedSince = captured.getFirstHeader("If-Modified-Since");
         assertEquals(lmDate, ifModifiedSince.getValue());
-        final Header ifNoneMatch =
-            captured.getFirstHeader("If-None-Match");
+        final Header ifNoneMatch = captured.getFirstHeader("If-None-Match");
         assertEquals(etag, ifNoneMatch.getValue());
     }
 
@@ -921,7 +953,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Expires",DateUtils.formatDate(oneSecondAgo));
         resp1.setHeader("Cache-Control", "public");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         final ClassicHttpRequest revalidate = new BasicClassicHttpRequest("GET", "/");
@@ -932,18 +964,12 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp2.setHeader("Expires", DateUtils.formatDate(oneSecondFromNow));
         resp2.setHeader("ETag","\"etag\"");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        eqRequest(revalidate),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2);
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(revalidate), Mockito.any())).thenReturn(resp2);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
-        assertEquals(HttpStatus.SC_OK,
-                result.getCode());
+        assertEquals(HttpStatus.SC_OK, result.getCode());
     }
 
     /* "When a client tries to revalidate a cache entry, and the response
@@ -968,32 +994,23 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
         resp1.setHeader("Cache-Control","max-age=5");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         final ClassicHttpResponse resp2 = HttpTestUtils.make304Response();
         resp2.setHeader("ETag","\"etag\"");
         resp2.setHeader("Date", DateUtils.formatDate(elevenSecondsAgo));
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
-        final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
-        resp3.setHeader("ETag","\"etag2\"");
-        resp3.setHeader("Date", DateUtils.formatDate(now));
-        resp3.setHeader("Cache-Control","max-age=5");
+        execute(req1);
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(cap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp3);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
-        replayMocks();
-        execute(req1);
         execute(req2);
-        verifyMocks();
 
-        final ClassicHttpRequest captured = cap.getValue();
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain, Mockito.times(3)).proceed(reqCapture.capture(), Mockito.any());
+
+        final ClassicHttpRequest captured = reqCapture.getValue();
         boolean hasMaxAge0 = false;
         boolean hasNoCache = false;
         final Iterator<HeaderElement> it = MessageSupport.iterate(captured, HttpHeaders.CACHE_CONTROL);
@@ -1036,8 +1053,6 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Vary","User-Agent");
         resp1.setHeader("Etag","\"etag1\"");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET","/");
         req2.setHeader("User-Agent","agent2");
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
@@ -1045,25 +1060,26 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp2.setHeader("Vary","User-Agent");
         resp2.setHeader("Etag","\"etag2\"");
 
-        backendExpectsAnyRequestAndReturn(resp2);
-
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET","/");
         req3.setHeader("User-Agent","agent3");
         final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(cap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp3);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         execute(req2);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(),Mockito.any())).thenReturn(resp3);
+
         execute(req3);
-        verifyMocks();
 
-        final ClassicHttpRequest captured = cap.getValue();
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain, Mockito.times(3)).proceed(reqCapture.capture(), Mockito.any());
+
+        final ClassicHttpRequest captured = reqCapture.getValue();
         boolean foundEtag1 = false;
         boolean foundEtag2 = false;
         for(final Header h : captured.getHeaders("If-None-Match")) {
@@ -1098,9 +1114,6 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Cache-Control", "max-age=3600");
         resp1.setHeader("ETag", "\"etag1\"");
 
-
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("User-Agent", "agent2");
 
@@ -1110,8 +1123,6 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp2.setHeader("Cache-Control", "max-age=3600");
         resp2.setHeader("ETag", "\"etag2\"");
 
-        backendExpectsAnyRequestAndReturn(resp2);
-
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
         req3.setHeader("User-Agent", "agent3");
 
@@ -1119,17 +1130,21 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp3.setHeader("Date", DateUtils.formatDate(now));
         resp3.setHeader("ETag", "\"etag1\"");
 
-        backendExpectsAnyRequestAndReturn(resp3);
-
         final ClassicHttpRequest req4 = new BasicClassicHttpRequest("GET", "/");
         req4.setHeader("User-Agent", "agent1");
 
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
+
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         execute(req2);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
+
         final ClassicHttpResponse result1 = execute(req3);
         final ClassicHttpResponse result2 = execute(req4);
-        verifyMocks();
 
         assertEquals(HttpStatus.SC_OK, result1.getCode());
         assertEquals("\"etag1\"", result1.getFirstHeader("ETag").getValue());
@@ -1149,7 +1164,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Cache-Control", "max-age=3600");
         resp1.setHeader("ETag", "\"etag1\"");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("User-Agent", "agent2");
@@ -1158,16 +1173,14 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp2.setHeader("Date", DateUtils.formatDate(now));
         resp2.setHeader("ETag", "\"etag1\"");
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
         req3.setHeader("User-Agent", "agent2");
 
-        replayMocks();
         execute(req1);
         execute(req2);
         execute(req3);
-        verifyMocks();
     }
 
     /* "If any of the existing cache entries contains only partial content
@@ -1186,8 +1199,6 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Vary", "User-Agent");
         resp1.setHeader("ETag", "\"etag1\"");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("User-Agent", "agent2");
         req2.setHeader("Range", "bytes=0-49");
@@ -1200,8 +1211,6 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp2.setHeader("Cache-Control","max-age=3600");
         resp2.setHeader("Date", DateUtils.formatDate(new Date()));
 
-        backendExpectsAnyRequestAndReturn(resp2);
-
         final ClassicHttpRequest req3 = HttpTestUtils.makeDefaultRequest();
         req3.setHeader("User-Agent", "agent3");
 
@@ -1210,19 +1219,22 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Vary", "User-Agent");
         resp1.setHeader("ETag", "\"etag3\"");
 
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(cap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp3);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         execute(req2);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
+
         execute(req3);
-        verifyMocks();
 
-        final ClassicHttpRequest captured = cap.getValue();
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain, Mockito.times(3)).proceed(reqCapture.capture(), Mockito.any());
+
+        final ClassicHttpRequest captured = reqCapture.getValue();
         final Iterator<HeaderElement> it = MessageSupport.iterate(captured, HttpHeaders.IF_NONE_MATCH);
         while (it.hasNext()) {
             final HeaderElement elt = it.next();
@@ -1247,7 +1259,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("ETag", "\"old-etag\"");
         resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = new HttpPost("http://foo.example.com/bar");
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
@@ -1255,18 +1267,16 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp2.setHeader("Date", DateUtils.formatDate(now));
         resp2.setHeader("Content-Location", "http://foo.example.com/");
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
         final ClassicHttpRequest req3 = new HttpGet("http://foo.example.com");
         final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
 
-        backendExpectsAnyRequestAndReturn(resp3);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
 
-        replayMocks();
         execute(req1);
         execute(req2);
         execute(req3);
-        verifyMocks();
     }
 
     /*
@@ -1285,11 +1295,9 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp2.setHeader("Content-Length","200");
         resp2.setHeader("Date", DateUtils.formatDate(now));
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
-        replayMocks();
         execute(req2);
-        verifyMocks();
     }
 
     @Test
@@ -1302,11 +1310,9 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp2.setHeader("Date", DateUtils.formatDate(now));
         resp2.setHeader("Via","1.0 someproxy");
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
-        replayMocks();
         execute(req2);
-        verifyMocks();
     }
 
     /*
@@ -1322,25 +1328,23 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
         resp1.setHeader("Cache-Control","max-age=3600");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("FROB", "/");
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
         resp2.setHeader("Cache-Control","max-age=3600");
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
         final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
         resp3.setHeader("ETag", "\"etag\"");
 
-        backendExpectsAnyRequestAndReturn(resp3);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
 
-        replayMocks();
         execute(req1);
         execute(req2);
         final ClassicHttpResponse result = execute(req3);
-        verifyMocks();
 
         assertTrue(HttpTestUtils.semanticallyTransparent(resp3, result));
     }
@@ -1353,44 +1357,46 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Cache-Control","max-age=3600");
         resp1.setHeader("Vary", "User-Agent");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("User-Agent", "agent2");
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
         resp2.setHeader("Cache-Control","max-age=3600");
         resp2.setHeader("Vary", "User-Agent");
 
-        backendExpectsAnyRequestAndReturn(resp2);
-
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("FROB", "/");
         req3.setHeader("User-Agent", "agent3");
         final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
         resp3.setHeader("Cache-Control","max-age=3600");
 
-        backendExpectsAnyRequestAndReturn(resp3);
-
         final ClassicHttpRequest req4 = new BasicClassicHttpRequest("GET", "/");
         req4.setHeader("User-Agent", "agent1");
         final ClassicHttpResponse resp4 = HttpTestUtils.make200Response();
         resp4.setHeader("ETag", "\"etag1\"");
 
-        backendExpectsAnyRequestAndReturn(resp4);
-
         final ClassicHttpRequest req5 = new BasicClassicHttpRequest("GET", "/");
         req5.setHeader("User-Agent", "agent2");
         final ClassicHttpResponse resp5 = HttpTestUtils.make200Response();
         resp5.setHeader("ETag", "\"etag2\"");
 
-        backendExpectsAnyRequestAndReturn(resp5);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         execute(req2);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
+
         execute(req3);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp4);
+
         final ClassicHttpResponse result4 = execute(req4);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp5);
+
         final ClassicHttpResponse result5 = execute(req5);
-        verifyMocks();
 
         assertTrue(HttpTestUtils.semanticallyTransparent(resp4, result4));
         assertTrue(HttpTestUtils.semanticallyTransparent(resp5, result5));
@@ -1411,7 +1417,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Cache-Control", "max-age=3600");
         resp1.setHeader("ETag", "\"etag1\"");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control", "max-age=0");
@@ -1420,15 +1426,13 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp2.setHeader("Cache-Control", "max-age=3600");
         resp2.setHeader("ETag", "\"etag2\"");
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
         final ClassicHttpRequest req3 = HttpTestUtils.makeDefaultRequest();
 
-        replayMocks();
         execute(req1);
         execute(req2);
         final ClassicHttpResponse result = execute(req3);
-        verifyMocks();
 
         assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result));
     }
@@ -1452,19 +1456,17 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Expires", DateUtils.formatDate(now));
         resp1.removeHeaders("Cache-Control");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control", "max-stale=1000");
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
         resp2.setHeader("ETag", "\"etag2\"");
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result));
     }
@@ -1477,19 +1479,17 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Expires", DateUtils.formatDate(tenSecondsAgo));
         resp1.removeHeaders("Cache-Control");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control", "max-stale=1000");
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
         resp2.setHeader("ETag", "\"etag2\"");
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result));
     }
@@ -1506,18 +1506,12 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         req1.setHeader("Cache-Control", "min-fresh=10, no-cache");
         req1.addHeader("Cache-Control", "max-stale=0, max-age=0");
 
-
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(cap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(HttpTestUtils.make200Response());
-
-        replayMocks();
         execute(req1);
-        verifyMocks();
 
-        final ClassicHttpRequest captured = cap.getValue();
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any());
+
+        final ClassicHttpRequest captured = reqCapture.getValue();
         boolean foundNoCache = false;
         boolean foundDisallowedDirective = false;
         final List<String> disallowed =
@@ -1550,12 +1544,9 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         final ClassicHttpRequest req = HttpTestUtils.makeDefaultRequest();
         req.setHeader("Cache-Control", "only-if-cached");
 
-        replayMocks();
         final ClassicHttpResponse result = execute(req);
-        verifyMocks();
 
-        assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT,
-                result.getCode());
+        assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT, result.getCode());
     }
 
     @Test
@@ -1564,15 +1555,13 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
         resp1.setHeader("Cache-Control","max-age=3600");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control", "only-if-cached");
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         assertTrue(HttpTestUtils.semanticallyTransparent(resp1, result));
     }
@@ -1584,18 +1573,15 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
         resp1.setHeader("Cache-Control","max-age=5");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control", "only-if-cached");
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
-        assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT,
-                result.getCode());
+        assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT, result.getCode());
     }
 
     @Test
@@ -1606,15 +1592,13 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
         resp1.setHeader("Cache-Control","max-age=5");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control", "max-stale=20, only-if-cached");
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         assertTrue(HttpTestUtils.semanticallyTransparent(resp1, result));
     }
@@ -1627,18 +1611,15 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Cache-Control", "max-age=300");
         resp1.setHeader("ETag","W/\"weak-sauce\"");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("If-None-Match","W/\"weak-sauce\"");
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode());
-
     }
 
 }
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestProtocolRequirements.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestProtocolRequirements.java
index 25d52ea..41fd750 100644
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestProtocolRequirements.java
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestProtocolRequirements.java
@@ -32,6 +32,7 @@ import java.net.SocketTimeoutException;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Random;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -41,12 +42,16 @@ import org.apache.hc.client5.http.HttpRoute;
 import org.apache.hc.client5.http.auth.StandardAuthScheme;
 import org.apache.hc.client5.http.cache.HttpCacheEntry;
 import org.apache.hc.client5.http.classic.ExecChain;
+import org.apache.hc.client5.http.classic.ExecRuntime;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
 import org.apache.hc.client5.http.utils.DateUtils;
 import org.apache.hc.core5.http.ClassicHttpRequest;
 import org.apache.hc.core5.http.ClassicHttpResponse;
 import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HeaderElement;
 import org.apache.hc.core5.http.HeaderElements;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpException;
 import org.apache.hc.core5.http.HttpHeaders;
 import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.HttpStatus;
@@ -54,16 +59,19 @@ import org.apache.hc.core5.http.HttpVersion;
 import org.apache.hc.core5.http.ProtocolVersion;
 import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
 import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
 import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
 import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
 import org.apache.hc.core5.http.message.BasicHeader;
 import org.apache.hc.core5.http.message.MessageSupport;
-import org.apache.hc.core5.util.ByteArrayBuffer;
-import org.easymock.Capture;
-import org.easymock.EasyMock;
 import org.junit.Assert;
-import org.junit.Ignore;
+import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
 
 /**
  * We are a conditionally-compliant HTTP/1.1 client with a cache. However, a lot
@@ -74,19 +82,68 @@ import org.junit.Test;
  * pass downstream to the backend HttpClient are are conditionally compliant
  * with the rules for an HTTP/1.1 client.
  */
-public class TestProtocolRequirements extends AbstractProtocolTest {
+@RunWith(MockitoJUnitRunner.class)
+public class TestProtocolRequirements {
+
+    static final int MAX_BYTES = 1024;
+    static final int MAX_ENTRIES = 100;
+    static final int ENTITY_LENGTH = 128;
+
+    HttpHost host;
+    HttpRoute route;
+    HttpEntity body;
+    HttpClientContext context;
+    @Mock
+    ExecChain mockExecChain;
+    @Mock
+    ExecRuntime mockExecRuntime;
+    @Mock
+    HttpCache mockCache;
+    ClassicHttpRequest request;
+    ClassicHttpResponse originResponse;
+    CacheConfig config;
+    CachingExec impl;
+    HttpCache cache;
+
+    @Before
+    public void setUp() throws Exception {
+        host = new HttpHost("foo.example.com", 80);
+
+        route = new HttpRoute(host);
+
+        body = HttpTestUtils.makeBody(ENTITY_LENGTH);
+
+        request = new BasicClassicHttpRequest("GET", "/foo");
+
+        context = HttpClientContext.create();
+
+        originResponse = HttpTestUtils.make200Response();
+
+        config = CacheConfig.custom()
+                .setMaxCacheEntries(MAX_ENTRIES)
+                .setMaxObjectSize(MAX_BYTES)
+                .build();
+
+        cache = new BasicHttpCache(config);
+        impl = new CachingExec(cache, null, config);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
+    }
+
+    public ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException {
+        return impl.execute(
+                ClassicRequestBuilder.copy(request).build(),
+                new ExecChain.Scope("test", route, request, mockExecRuntime, context),
+                mockExecChain);
+    }
 
     @Test
     public void testCacheMissOnGETUsesOriginResponse() throws Exception {
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        eqRequest(request),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-        replayMocks();
+
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(request), Mockito.any())).thenReturn(originResponse);
 
         final ClassicHttpResponse result = execute(request);
 
-        verifyMocks();
         Assert.assertTrue(HttpTestUtils.semanticallyTransparent(originResponse, result));
     }
 
@@ -109,15 +166,10 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         request = new BasicClassicHttpRequest("GET", "/foo");
         request.setVersion(new ProtocolVersion("HTTP", 2, 13));
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        eqRequest(request),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(request), Mockito.any())).thenReturn(originResponse);
 
         final ClassicHttpResponse result = execute(request);
 
-        verifyMocks();
         Assert.assertSame(originResponse, result);
     }
 
@@ -130,15 +182,10 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpRequest downgraded = new BasicClassicHttpRequest("GET", "/foo");
         downgraded.setVersion(HttpVersion.HTTP_1_1);
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        eqRequest(downgraded),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(downgraded), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
 
-        verifyMocks();
         Assert.assertTrue(HttpTestUtils.semanticallyTransparent(originResponse, result));
     }
 
@@ -159,15 +206,10 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpRequest upgraded = new BasicClassicHttpRequest("GET", "/foo");
         upgraded.setVersion(HttpVersion.HTTP_1_1);
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        eqRequest(upgraded),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(upgraded), Mockito.any())).thenReturn(originResponse);
 
         final ClassicHttpResponse result = execute(request);
 
-        verifyMocks();
         Assert.assertTrue(HttpTestUtils.semanticallyTransparent(originResponse, result));
     }
 
@@ -189,15 +231,10 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
 
         // not testing this internal behavior in this test, just want
         // to check the protocol version that comes out the other end
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         final ClassicHttpResponse result = execute(request);
 
-        verifyMocks();
         Assert.assertEquals(HttpVersion.HTTP_1_1, result.getVersion());
     }
 
@@ -206,15 +243,10 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         request = new BasicClassicHttpRequest("GET", "/foo");
         request.setVersion(new ProtocolVersion("HTTP", 1, 0));
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         final ClassicHttpResponse result = execute(request);
 
-        verifyMocks();
         Assert.assertEquals(HttpVersion.HTTP_1_1, result.getVersion());
     }
 
@@ -234,15 +266,9 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         downgraded.removeHeaders("Connection");
         downgraded.addHeader("X-Unknown-Header", "some-value");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        eqRequest(downgraded),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(downgraded), Mockito.any())).thenReturn(originResponse);
 
         execute(request);
-
-        verifyMocks();
     }
 
     /*
@@ -257,16 +283,10 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpRequest originalRequest = new BasicClassicHttpRequest("GET", "/foo");
         originalRequest.setVersion(new ProtocolVersion("HTTP", 1, 0));
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         final ClassicHttpResponse result = execute(originalRequest);
 
-        verifyMocks();
-
         Assert.assertNull(result.getFirstHeader("TE"));
         Assert.assertNull(result.getFirstHeader("Transfer-Encoding"));
     }
@@ -285,26 +305,19 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
      * http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
      */
     private void testOrderOfMultipleHeadersIsPreservedOnRequests(final String h, final ClassicHttpRequest request) throws Exception {
-        final Capture<ClassicHttpRequest> reqCapture = EasyMock.newCapture();
-
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(reqCapture),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         execute(request);
 
-        verifyMocks();
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any());
 
         final ClassicHttpRequest forwarded = reqCapture.getValue();
-        Assert.assertNotNull(forwarded);
         final String expected = HttpTestUtils.getCanonicalHeaderValue(request, h);
         final String actual = HttpTestUtils.getCanonicalHeaderValue(forwarded, h);
         if (!actual.contains(expected)) {
             Assert.assertEquals(expected, actual);
         }
-
     }
 
     @Test
@@ -404,16 +417,10 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
     }
 
     private void testOrderOfMultipleHeadersIsPreservedOnResponses(final String h) throws Exception {
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         final ClassicHttpResponse result = execute(request);
 
-        verifyMocks();
-
         Assert.assertNotNull(result);
         Assert.assertEquals(HttpTestUtils.getCanonicalHeaderValue(originResponse, h), HttpTestUtils
                 .getCanonicalHeaderValue(result, h));
@@ -480,25 +487,18 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
      */
     private void testUnknownResponseStatusCodeIsNotCached(final int code) throws Exception {
 
-        emptyMockCacheExpectsNoPuts();
-
         originResponse = new BasicClassicHttpResponse(code, "Moo");
         originResponse.setHeader("Date", DateUtils.formatDate(new Date()));
         originResponse.setHeader("Server", "MockOrigin/1.0");
         originResponse.setHeader("Cache-Control", "max-age=3600");
         originResponse.setEntity(body);
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         execute(request);
 
         // in particular, there were no storage calls on the cache
-        verifyMocks();
+        Mockito.verifyNoInteractions(mockCache);
     }
 
     @Test
@@ -529,18 +529,13 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
     @Test
     public void testUnknownHeadersOnRequestsAreForwarded() throws Exception {
         request.addHeader("X-Unknown-Header", "blahblah");
-        final Capture<ClassicHttpRequest> reqCap = EasyMock.newCapture();
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(reqCap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         execute(request);
 
-        verifyMocks();
-        final ClassicHttpRequest forwarded = reqCap.getValue();
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any());
+        final ClassicHttpRequest forwarded = reqCapture.getValue();
         final Header[] hdrs = forwarded.getHeaders("X-Unknown-Header");
         Assert.assertEquals(1, hdrs.length);
         Assert.assertEquals("blahblah", hdrs[0].getValue());
@@ -549,16 +544,10 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
     @Test
     public void testUnknownHeadersOnResponsesAreForwarded() throws Exception {
         originResponse.addHeader("X-Unknown-Header", "blahblah");
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         final ClassicHttpResponse result = execute(request);
 
-        verifyMocks();
         final Header[] hdrs = result.getHeaders("X-Unknown-Header");
         Assert.assertEquals(1, hdrs.length);
         Assert.assertEquals("blahblah", hdrs[0].getValue());
@@ -578,20 +567,13 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         post.setHeader("Content-Length", "128");
         post.setEntity(new StringEntity("whatever"));
 
-        final Capture<ClassicHttpRequest> reqCap = EasyMock.newCapture();
-
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(reqCap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         execute(post);
 
-        verifyMocks();
-
-        final ClassicHttpRequest forwarded = reqCap.getValue();
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any());
+        final ClassicHttpRequest forwarded = reqCapture.getValue();
         boolean foundExpect = false;
         final Iterator<HeaderElement> it = MessageSupport.iterate(forwarded, HttpHeaders.EXPECT);
         while (it.hasNext()) {
@@ -617,20 +599,13 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         post.setHeader("Content-Length", "128");
         post.setEntity(new StringEntity("whatever"));
 
-        final Capture<ClassicHttpRequest> reqCap = EasyMock.newCapture();
-
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(reqCap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         execute(post);
 
-        verifyMocks();
-
-        final ClassicHttpRequest forwarded = reqCap.getValue();
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any());
+        final ClassicHttpRequest forwarded = reqCapture.getValue();
         boolean foundExpect = false;
         final Iterator<HeaderElement> it = MessageSupport.iterate(forwarded, HttpHeaders.EXPECT);
         while (it.hasNext()) {
@@ -681,11 +656,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         post.setHeader("Content-Length", "128");
 
         originResponse = new BasicClassicHttpResponse(100, "Continue");
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         try {
             // if a 100 response gets up to us from the HttpClient
@@ -694,8 +665,6 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
             Assert.fail("should have thrown an exception");
         } catch (final ClientProtocolException expected) {
         }
-
-        verifyMocks();
     }
 
     /*
@@ -705,20 +674,14 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
      */
     @Test
     public void testResponsesToOPTIONSAreNotCacheable() throws Exception {
-        emptyMockCacheExpectsNoPuts();
         request = new BasicClassicHttpRequest("OPTIONS", "/");
         originResponse.addHeader("Cache-Control", "max-age=3600");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         execute(request);
 
-        verifyMocks();
+        Mockito.verifyNoInteractions(mockCache);
     }
 
     /*
@@ -734,15 +697,10 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse.setEntity(null);
         originResponse.setHeader("Content-Length", "0");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         final ClassicHttpResponse result = execute(request);
 
-        verifyMocks();
         final Header contentLength = result.getFirstHeader("Content-Length");
         Assert.assertNotNull(contentLength);
         Assert.assertEquals("0", contentLength.getValue());
@@ -762,9 +720,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         request = new BasicClassicHttpRequest("OPTIONS", "*");
         request.setHeader("Max-Forwards", "0");
 
-        replayMocks();
         execute(request);
-        verifyMocks();
     }
 
     /*
@@ -779,18 +735,14 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         request = new BasicClassicHttpRequest("OPTIONS", "*");
         request.setHeader("Max-Forwards", "7");
 
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(cap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-
-        replayMocks();
         execute(request);
-        verifyMocks();
 
-        final ClassicHttpRequest captured = cap.getValue();
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any());
+
+        final ClassicHttpRequest captured = reqCapture.getValue();
         Assert.assertEquals("6", captured.getFirstHeader("Max-Forwards").getValue());
     }
 
@@ -803,17 +755,14 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
     @Test
     public void testDoesNotAddAMaxForwardsHeaderToForwardedOPTIONSRequests() throws Exception {
         request = new BasicClassicHttpRequest("OPTIONS", "/");
-        final Capture<ClassicHttpRequest> reqCap = EasyMock.newCapture();
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(reqCap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         execute(request);
-        verifyMocks();
 
-        final ClassicHttpRequest forwarded = reqCap.getValue();
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any());
+
+        final ClassicHttpRequest forwarded = reqCapture.getValue();
         Assert.assertNull(forwarded.getFirstHeader("Max-Forwards"));
     }
 
@@ -826,17 +775,10 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
     @Test
     public void testResponseToAHEADRequestMustNotHaveABody() throws Exception {
         request = new BasicClassicHttpRequest("HEAD", "/");
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         final ClassicHttpResponse result = execute(request);
 
-        verifyMocks();
-
         Assert.assertTrue(result.getEntity() == null || result.getEntity().getContentLength() == 0);
     }
 
@@ -870,26 +812,13 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
         resp3.setHeader(eHeader, newVal);
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        eqRequest(req1),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        eqRequest(req2),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp3);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(req1), Mockito.any())).thenReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(req2), Mockito.any())).thenReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(req3), Mockito.any())).thenReturn(resp3);
 
         execute(req1);
         execute(req2);
         execute(req3);
-
-        verifyMocks();
     }
 
     @Test
@@ -927,7 +856,6 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
      */
     @Test
     public void testResponsesToPOSTWithoutCacheControlOrExpiresAreNotCached() throws Exception {
-        emptyMockCacheExpectsNoPuts();
 
         final BasicClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/");
         post.setHeader("Content-Length", "128");
@@ -936,16 +864,11 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse.removeHeaders("Cache-Control");
         originResponse.removeHeaders("Expires");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         execute(post);
 
-        verifyMocks();
+        Mockito.verifyNoInteractions(mockCache);
     }
 
     /*
@@ -955,7 +878,6 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
      */
     @Test
     public void testResponsesToPUTsAreNotCached() throws Exception {
-        emptyMockCacheExpectsNoPuts();
 
         final BasicClassicHttpRequest put = new BasicClassicHttpRequest("PUT", "/");
         put.setEntity(HttpTestUtils.makeBody(128));
@@ -963,16 +885,11 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
 
         originResponse.setHeader("Cache-Control", "max-age=3600");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         execute(put);
 
-        verifyMocks();
+        Mockito.verifyNoInteractions(mockCache);
     }
 
     /*
@@ -982,21 +899,15 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
      */
     @Test
     public void testResponsesToDELETEsAreNotCached() throws Exception {
-        emptyMockCacheExpectsNoPuts();
 
         request = new BasicClassicHttpRequest("DELETE", "/");
         originResponse.setHeader("Cache-Control", "max-age=3600");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         execute(request);
 
-        verifyMocks();
+        Mockito.verifyNoInteractions(mockCache);
     }
 
     /*
@@ -1006,44 +917,15 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
      */
     @Test
     public void testResponsesToTRACEsAreNotCached() throws Exception {
-        emptyMockCacheExpectsNoPuts();
 
         request = new BasicClassicHttpRequest("TRACE", "/");
         originResponse.setHeader("Cache-Control", "max-age=3600");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         execute(request);
 
-        verifyMocks();
-    }
-
-    /*
-     * "The 204 response MUST NOT include a message-body, and thus is always
-     * terminated by the first empty line after the header fields."
-     *
-     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.5
-     */
-    @Test
-    public void test204ResponsesDoNotContainMessageBodies() throws Exception {
-        originResponse = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content");
-        originResponse.setEntity(HttpTestUtils.makeBody(entityLength));
-
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-
-        replayMocks();
-
-        final ClassicHttpResponse result = execute(request);
-
-        verifyMocks();
+        Mockito.verifyNoInteractions(mockCache);
     }
 
     /*
@@ -1076,12 +958,10 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("Range", "bytes=0-50");
 
-        backendExpectsAnyRequestAndReturn(resp1).times(1, 2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         if (HttpStatus.SC_PARTIAL_CONTENT == result.getCode()) {
             if (result.getFirstHeader("Content-Range") == null) {
@@ -1092,6 +972,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
                 Assert.assertFalse("".equals(elt.getParameterByName("boundary").getValue().trim()));
             }
         }
+        Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any());
     }
 
     @Test
@@ -1105,12 +986,10 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("Range", "bytes=0-50");
 
-        backendExpectsAnyRequestAndReturn(resp1).times(1, 2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         if (HttpStatus.SC_PARTIAL_CONTENT == result.getCode()) {
             final Header h = result.getFirstHeader("Content-Length");
@@ -1125,6 +1004,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
                 Assert.assertEquals(contentLength, bytesRead);
             }
         }
+        Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any());
     }
 
     @Test
@@ -1137,16 +1017,15 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("Range", "bytes=0-50");
 
-        backendExpectsAnyRequestAndReturn(resp1).times(1, 2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         if (HttpStatus.SC_PARTIAL_CONTENT == result.getCode()) {
             Assert.assertNotNull(result.getFirstHeader("Date"));
         }
+        Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any());
     }
 
     @Test
@@ -1159,18 +1038,12 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse.setHeader("Content-Range", "bytes 0-499/1234");
         originResponse.removeHeaders("Date");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         final ClassicHttpResponse result = execute(request);
         Assert.assertTrue(result.getCode() != HttpStatus.SC_PARTIAL_CONTENT
                 || result.getFirstHeader("Date") != null);
 
-        verifyMocks();
     }
 
     @Test
@@ -1183,18 +1056,15 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.addHeader("Range", "bytes=0-50");
 
-        backendExpectsAnyRequest().andReturn(originResponse).times(1, 2);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
 
-        verifyMocks();
-
         if (result.getCode() == HttpStatus.SC_PARTIAL_CONTENT) {
             Assert.assertNotNull(result.getFirstHeader("ETag"));
         }
+        Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any());
     }
 
     @Test
@@ -1207,18 +1077,15 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.addHeader("Range", "bytes=0-50");
 
-        backendExpectsAnyRequest().andReturn(originResponse).times(1, 2);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
 
-        verifyMocks();
-
         if (result.getCode() == HttpStatus.SC_PARTIAL_CONTENT) {
             Assert.assertNotNull(result.getFirstHeader("Content-Location"));
         }
+        Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any());
     }
 
     @Test
@@ -1249,22 +1116,22 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         req3.addHeader("Range", "bytes=0-50");
         req3.addHeader("Accept-Encoding", "gzip");
 
-        backendExpectsAnyRequest().andReturn(originResponse);
-        backendExpectsAnyRequestAndReturn(originResponse2).times(1, 2);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse2);
+
         execute(req2);
         final ClassicHttpResponse result = execute(req3);
 
-        verifyMocks();
 
         if (result.getCode() == HttpStatus.SC_PARTIAL_CONTENT) {
             Assert.assertNotNull(result.getFirstHeader("Expires"));
             Assert.assertNotNull(result.getFirstHeader("Cache-Control"));
             Assert.assertNotNull(result.getFirstHeader("Vary"));
         }
+        Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any());
     }
 
     /*
@@ -1297,15 +1164,11 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         req2.addHeader("If-Range", "W/\"weak-tag\"");
         req2.addHeader("Range", "bytes=0-50");
 
-        backendExpectsAnyRequest().andReturn(originResponse).times(1, 2);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
 
-        verifyMocks();
-
         if (result.getCode() == HttpStatus.SC_PARTIAL_CONTENT) {
             Assert.assertNull(result.getFirstHeader("Allow"));
             Assert.assertNull(result.getFirstHeader("Content-Encoding"));
@@ -1313,6 +1176,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
             Assert.assertNull(result.getFirstHeader("Content-MD5"));
             Assert.assertNull(result.getFirstHeader("Last-Modified"));
         }
+        Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any());
     }
 
     /*
@@ -1343,15 +1207,11 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         req2.addHeader("If-Range", "\"strong-tag\"");
         req2.addHeader("Range", "bytes=0-50");
 
-        backendExpectsAnyRequest().andReturn(originResponse).times(1, 2);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
 
-        verifyMocks();
-
         if (result.getCode() == HttpStatus.SC_PARTIAL_CONTENT) {
             Assert.assertEquals("GET,HEAD", result.getFirstHeader("Allow").getValue());
             Assert.assertEquals("max-age=3600", result.getFirstHeader("Cache-Control").getValue());
@@ -1362,6 +1222,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
             Assert.assertEquals(originResponse.getFirstHeader("Last-Modified").getValue(), result
                     .getFirstHeader("Last-Modified").getValue());
         }
+        Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any());
     }
 
     /*
@@ -1401,33 +1262,16 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
 
         final Date inTwoSeconds = new Date(now.getTime() + 2000L);
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
-        final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
-        resp3.setHeader("Date", DateUtils.formatDate(inTwoSeconds));
-        resp3.setHeader("Cache-Control", "max-age=3600");
-        resp3.setHeader("ETag", "\"etag2\"");
-        final byte[] bytes3 = new byte[128];
-        Arrays.fill(bytes3, (byte) 2);
-        resp3.setEntity(new ByteArrayEntity(bytes3, null));
-
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1);
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2);
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp3).times(0, 1);
-        replayMocks();
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         execute(req2);
-        final ClassicHttpResponse result = execute(req3);
 
-        verifyMocks();
+        final ClassicHttpResponse result = execute(req3);
 
         final InputStream i = result.getEntity().getContent();
         int b;
@@ -1443,6 +1287,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         }
         i.close();
         Assert.assertFalse(found1 && found2); // mixture of content
+        Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any());
     }
 
     @Test
@@ -1476,33 +1321,16 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
 
         final Date inTwoSeconds = new Date(now.getTime() + 2000L);
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
-        final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
-        resp3.setHeader("Date", DateUtils.formatDate(inTwoSeconds));
-        resp3.setHeader("Cache-Control", "max-age=3600");
-        resp3.setHeader("ETag", "\"etag2\"");
-        final byte[] bytes3 = new byte[128];
-        Arrays.fill(bytes3, (byte) 2);
-        resp3.setEntity(new ByteArrayEntity(bytes3, null));
-
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1);
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2);
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp3).times(0, 1);
-        replayMocks();
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         execute(req2);
-        final ClassicHttpResponse result = execute(req3);
 
-        verifyMocks();
+        final ClassicHttpResponse result = execute(req3);
 
         final InputStream i = result.getEntity().getContent();
         int b;
@@ -1518,6 +1346,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         }
         i.close();
         Assert.assertFalse(found1 && found2); // mixture of content
+        Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any());
     }
 
     /*
@@ -1529,9 +1358,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
     @Test
     public void test206ResponsesAreNotCachedIfTheCacheDoesNotSupportRangeAndContentRangeHeaders() throws Exception {
 
-        if (!supportsRangeAndContentRangeHeaders(impl)) {
-            emptyMockCacheExpectsNoPuts();
-
+        if (!impl.supportsRangeAndContentRangeHeaders()) {
             request = new BasicClassicHttpRequest("GET", "/");
             request.addHeader("Range", "bytes=0-50");
 
@@ -1542,14 +1369,10 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
             new Random().nextBytes(bytes);
             originResponse.setEntity(new ByteArrayEntity(bytes, null));
 
-            EasyMock.expect(
-                    mockExecChain.proceed(
-                            EasyMock.isA(ClassicHttpRequest.class),
-                            EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
+            Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-            replayMocks();
             execute(request);
-            verifyMocks();
+            Mockito.verifyNoInteractions(mockCache);
         }
     }
 
@@ -1561,7 +1384,6 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
      */
     @Test
     public void test303ResponsesAreNotCached() throws Exception {
-        emptyMockCacheExpectsNoPuts();
 
         request = new BasicClassicHttpRequest("GET", "/");
 
@@ -1571,44 +1393,13 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse.setHeader("Cache-Control", "max-age=3600");
         originResponse.setHeader("Content-Type", "application/x-cachingclient-test");
         originResponse.setHeader("Location", "http://foo.example.com/other");
-        originResponse.setEntity(HttpTestUtils.makeBody(entityLength));
+        originResponse.setEntity(HttpTestUtils.makeBody(ENTITY_LENGTH));
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         execute(request);
-        verifyMocks();
-    }
-
-    /*
-     * "The 304 response MUST NOT contain a message-body, and thus is always
-     * terminated by the first empty line after the header fields."
-     *
-     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
-     */
-    @Test
-    public void test304ResponseDoesNotContainABody() throws Exception {
-        request.setHeader("If-None-Match", "\"etag\"");
 
-        originResponse = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED,"Not Modified");
-        originResponse.setHeader("Date", DateUtils.formatDate(new Date()));
-        originResponse.setHeader("Server", "MockServer/1.0");
-        originResponse.setHeader("Content-Length", "128");
-        originResponse.setEntity(HttpTestUtils.makeBody(entityLength));
-
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-
-        replayMocks();
-
-        final ClassicHttpResponse result = execute(request);
-
-        verifyMocks();
+        Mockito.verifyNoInteractions(mockCache);
     }
 
     /*
@@ -1628,15 +1419,10 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse.setHeader("Server", "MockServer/1.0");
         originResponse.setHeader("ETag", "\"etag\"");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         final ClassicHttpResponse result = execute(request);
 
-        verifyMocks();
         Assert.assertNotNull(result.getFirstHeader("Date"));
     }
 
@@ -1650,19 +1436,15 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("If-None-Match", "\"etag\"");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse).times(1, 2);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
 
-        verifyMocks();
         if (result.getCode() == HttpStatus.SC_NOT_MODIFIED) {
             Assert.assertNotNull(result.getFirstHeader("Date"));
         }
+        Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any());
     }
 
     /*
@@ -1681,19 +1463,15 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("If-None-Match", "\"etag\"");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse).times(1, 2);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
 
-        verifyMocks();
         if (result.getCode() == HttpStatus.SC_NOT_MODIFIED) {
             Assert.assertNotNull(result.getFirstHeader("ETag"));
         }
+        Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any());
     }
 
     @Test
@@ -1706,19 +1484,15 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("If-None-Match", "\"etag\"");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse).times(1, 2);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
 
-        verifyMocks();
         if (result.getCode() == HttpStatus.SC_NOT_MODIFIED) {
             Assert.assertNotNull(result.getFirstHeader("Content-Location"));
         }
+        Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any());
     }
 
     /*
@@ -1742,7 +1516,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp1.setHeader("Cache-Control", "max-age=7200");
         resp1.setHeader("Expires", DateUtils.formatDate(inTwoHours));
         resp1.setHeader("Vary", "Accept-Encoding");
-        resp1.setEntity(HttpTestUtils.makeBody(entityLength));
+        resp1.setEntity(HttpTestUtils.makeBody(ENTITY_LENGTH));
 
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req1.setHeader("Accept-Encoding", "gzip");
@@ -1753,33 +1527,27 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp2.setHeader("Cache-Control", "max-age=3600");
         resp2.setHeader("Expires", DateUtils.formatDate(inTwoHours));
         resp2.setHeader("Vary", "Accept-Encoding");
-        resp2.setEntity(HttpTestUtils.makeBody(entityLength));
+        resp2.setEntity(HttpTestUtils.makeBody(ENTITY_LENGTH));
 
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
         req3.setHeader("Accept-Encoding", "gzip");
         req3.setHeader("If-None-Match", "\"v2\"");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1);
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2).times(1, 2);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         execute(req2);
         final ClassicHttpResponse result = execute(req3);
 
-        verifyMocks();
-
         if (result.getCode() == HttpStatus.SC_NOT_MODIFIED) {
             Assert.assertNotNull(result.getFirstHeader("Expires"));
             Assert.assertNotNull(result.getFirstHeader("Cache-Control"));
             Assert.assertNotNull(result.getFirstHeader("Vary"));
         }
+        Mockito.verify(mockExecChain, Mockito.times(3)).proceed(Mockito.any(), Mockito.any());
     }
 
     /*
@@ -1811,17 +1579,11 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("If-None-Match", "W/\"v1\"");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1).times(1, 2);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(req1), Mockito.any())).thenReturn(resp1);
 
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
 
-        verifyMocks();
-
         if (result.getCode() == HttpStatus.SC_NOT_MODIFIED) {
             Assert.assertNull(result.getFirstHeader("Allow"));
             Assert.assertNull(result.getFirstHeader("Content-Encoding"));
@@ -1830,6 +1592,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
             Assert.assertNull(result.getFirstHeader("Content-Type"));
             Assert.assertNull(result.getFirstHeader("Last-Modified"));
         }
+        Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any());
     }
 
     /*
@@ -1854,44 +1617,23 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("Cache-Control", "max-age=0,max-stale=0");
 
-        // updated ETag provided to a conditional revalidation
-        final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED,
-                "Not Modified");
-        resp2.setHeader("Date", DateUtils.formatDate(now));
-        resp2.setHeader("Server", "MockServer/1.0");
-        resp2.setHeader("ETag", "\"etag2\"");
-
-        // conditional validation uses If-None-Match
-        final ClassicHttpRequest conditionalValidation = new BasicClassicHttpRequest("GET", "/");
-        conditionalValidation.setHeader("If-None-Match", "\"etag1\"");
-
         // unconditional validation doesn't use If-None-Match
         final ClassicHttpRequest unconditionalValidation = new BasicClassicHttpRequest("GET", "/");
         // new response to unconditional validation provides new body
-        final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
+        final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
         resp1.setHeader("ETag", "\"etag2\"");
         resp1.setHeader("Cache-Control", "max-age=3600");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
         // this next one will happen once if the cache tries to
         // conditionally validate, zero if it goes full revalidation
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        eqRequest(conditionalValidation),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2).times(0, 1);
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        eqRequest(unconditionalValidation),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp3);
-        replayMocks();
+
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(unconditionalValidation), Mockito.any())).thenReturn(resp2);
 
         execute(req1);
         execute(req2);
 
-        verifyMocks();
+        Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any());
     }
 
     /*
@@ -1919,8 +1661,6 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpRequest conditionalValidationRequest = new BasicClassicHttpRequest("GET", "/");
         conditionalValidationRequest.setHeader("If-None-Match", "\"etag\"");
 
-        final ClassicHttpRequest unconditionalValidationRequest = new BasicClassicHttpRequest("GET", "/");
-
         // to be used if the cache generates a conditional validation
         final ClassicHttpResponse conditionalResponse = HttpTestUtils.make304Response();
         conditionalResponse.setHeader("Date", DateUtils.formatDate(inFiveSeconds));
@@ -1933,37 +1673,16 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         unconditionalResponse.setHeader("Date", DateUtils.formatDate(inFiveSeconds));
         unconditionalResponse.setHeader("ETag", "\"etag\"");
 
-        final Capture<ClassicHttpRequest> cap1 = EasyMock.newCapture();
-        final Capture<ClassicHttpRequest> cap2 = EasyMock.newCapture();
-
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(cachedResponse);
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.and(eqRequest(conditionalValidationRequest), EasyMock.capture(cap1)),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(conditionalResponse).times(0, 1);
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.and(eqRequest(unconditionalValidationRequest), EasyMock.capture(cap2)),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(unconditionalResponse).times(0, 1);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(cachedResponse);
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(conditionalValidationRequest), Mockito.any())).thenReturn(conditionalResponse);
 
         execute(initialRequest);
         final ClassicHttpResponse result = execute(secondRequest);
 
-        verifyMocks();
+        Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any());
 
-        Assert.assertTrue((cap1.hasCaptured() && !cap2.hasCaptured())
-                || (!cap1.hasCaptured() && cap2.hasCaptured()));
-
-        if (cap1.hasCaptured()) {
-            Assert.assertEquals(DateUtils.formatDate(inFiveSeconds), result.getFirstHeader("Date")
-                    .getValue());
-            Assert.assertEquals("junk", result.getFirstHeader("X-Extra").getValue());
-        }
+        Assert.assertEquals(DateUtils.formatDate(inFiveSeconds), result.getFirstHeader("Date").getValue());
+        Assert.assertEquals("junk", result.getFirstHeader("X-Extra").getValue());
     }
 
     /*
@@ -1978,18 +1697,11 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse = new BasicClassicHttpResponse(401, "Unauthorized");
         originResponse.setHeader("WWW-Authenticate", "x-scheme x-param");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         final ClassicHttpResponse result = execute(request);
-        if (result.getCode() == 401) {
-            Assert.assertNotNull(result.getFirstHeader("WWW-Authenticate"));
-        }
-
-        verifyMocks();
+        Assert.assertEquals(401, result.getCode());
+        Assert.assertNotNull(result.getFirstHeader("WWW-Authenticate"));
     }
 
     /*
@@ -2003,16 +1715,11 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse = new BasicClassicHttpResponse(405, "Method Not Allowed");
         originResponse.setHeader("Allow", "GET, HEAD");
 
-        backendExpectsAnyRequest().andReturn(originResponse);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         final ClassicHttpResponse result = execute(request);
-        if (result.getCode() == 405) {
-            Assert.assertNotNull(result.getFirstHeader("Allow"));
-        }
-
-        verifyMocks();
+        Assert.assertEquals(405, result.getCode());
+        Assert.assertNotNull(result.getFirstHeader("Allow"));
     }
 
     /*
@@ -2027,18 +1734,11 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse = new BasicClassicHttpResponse(407, "Proxy Authentication Required");
         originResponse.setHeader("Proxy-Authenticate", "x-scheme x-param");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
         final ClassicHttpResponse result = execute(request);
-        if (result.getCode() == 407) {
-            Assert.assertNotNull(result.getFirstHeader("Proxy-Authenticate"));
-        }
-
-        verifyMocks();
+        Assert.assertEquals(407, result.getCode());
+        Assert.assertNotNull(result.getFirstHeader("Proxy-Authenticate"));
     }
 
     /*
@@ -2051,28 +1751,22 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
     public void testMustNotAddMultipartByteRangeContentTypeTo416Response() throws Exception {
         originResponse = new BasicClassicHttpResponse(416, "Requested Range Not Satisfiable");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
 
-        if (result.getCode() == 416) {
-            final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.CONTENT_TYPE);
-            while (it.hasNext()) {
-                final HeaderElement elt = it.next();
-                Assert.assertFalse("multipart/byteranges".equalsIgnoreCase(elt.getName()));
-            }
+        Assert.assertEquals(416, result.getCode());
+        final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.CONTENT_TYPE);
+        while (it.hasNext()) {
+            final HeaderElement elt = it.next();
+            Assert.assertFalse("multipart/byteranges".equalsIgnoreCase(elt.getName()));
         }
     }
 
     @Test
     public void testMustNotUseMultipartByteRangeContentTypeOnCacheGenerated416Responses() throws Exception {
 
-        originResponse.setEntity(HttpTestUtils.makeBody(entityLength));
+        originResponse.setEntity(HttpTestUtils.makeBody(ENTITY_LENGTH));
         originResponse.setHeader("Content-Length", "128");
         originResponse.setHeader("Cache-Control", "max-age=3600");
 
@@ -2082,30 +1776,22 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpResponse orig416 = new BasicClassicHttpResponse(416,
                 "Requested Range Not Satisfiable");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
         // cache may 416 me right away if it understands byte ranges,
         // ok to delegate to origin though
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(orig416).times(0, 1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(rangeReq), Mockito.any())).thenReturn(orig416);
 
-        replayMocks();
         execute(request);
         final ClassicHttpResponse result = execute(rangeReq);
-        verifyMocks();
 
         // might have gotten a 416 from the origin or the cache
-        if (result.getCode() == 416) {
-            final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.CONTENT_TYPE);
-            while (it.hasNext()) {
-                final HeaderElement elt = it.next();
-                Assert.assertFalse("multipart/byteranges".equalsIgnoreCase(elt.getName()));
-            }
+        Assert.assertEquals(416, result.getCode());
+        final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.CONTENT_TYPE);
+        while (it.hasNext()) {
+            final HeaderElement elt = it.next();
+            Assert.assertFalse("multipart/byteranges".equalsIgnoreCase(elt.getName()));
         }
+        Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any());
     }
 
     /*
@@ -2167,25 +1853,26 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         notModified.setHeader("Date", DateUtils.formatDate(now));
         notModified.setHeader("ETag", "\"etag\"");
 
-        EasyMock.expect(
-                mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request)))
-                .andReturn(entry);
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        eqRequest(validate),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(notModified);
-        EasyMock.expect(mockCache.updateCacheEntry(
-                EasyMock.eq(host),
-                eqRequest(request),
-                EasyMock.eq(entry),
-                eqResponse(notModified),
-                EasyMock.isA(Date.class),
-                EasyMock.isA(Date.class)))
-            .andReturn(HttpTestUtils.makeCacheEntry());
-
-        replayMocks();
+        Mockito.when(mockCache.getCacheEntry(Mockito.eq(host), RequestEquivalent.eq(request))).thenReturn(entry);
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(validate), Mockito.any())).thenReturn(notModified);
+        Mockito.when(mockCache.updateCacheEntry(
+                Mockito.eq(host),
+                RequestEquivalent.eq(request),
+                Mockito.eq(entry),
+                ResponseEquivalent.eq(notModified),
+                Mockito.any(),
+                Mockito.any()))
+                .thenReturn(HttpTestUtils.makeCacheEntry());
+
         execute(request);
-        verifyMocks();
+
+        Mockito.verify(mockCache).updateCacheEntry(
+                Mockito.any(),
+                Mockito.any(),
+                Mockito.any(),
+                Mockito.any(),
+                Mockito.any(),
+                Mockito.any());
     }
 
     @Test
@@ -2210,11 +1897,9 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         impl = new CachingExec(mockCache, null, config);
         request = new BasicClassicHttpRequest("GET", "/thing");
 
-        EasyMock.expect(mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request))).andReturn(entry);
+        Mockito.when(mockCache.getCacheEntry(Mockito.eq(host), RequestEquivalent.eq(request))).thenReturn(entry);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
 
         Assert.assertEquals(200, result.getCode());
     }
@@ -2256,31 +1941,21 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         impl = new CachingExec(mockCache, null, config);
         request = new BasicClassicHttpRequest("GET", "/thing");
 
-        EasyMock.expect(mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request))).andReturn(entry);
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andThrow(
-                new IOException("can't talk to origin!")).anyTimes();
-
-        replayMocks();
+        Mockito.when(mockCache.getCacheEntry(Mockito.eq(host), RequestEquivalent.eq(request))).thenReturn(entry);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenThrow(
+                new IOException("can't talk to origin!"));
 
         final ClassicHttpResponse result = execute(request);
 
-        verifyMocks();
-
         final int status = result.getCode();
-        if (status == 200) {
-            boolean foundWarning = false;
-            for (final Header h : result.getHeaders("Warning")) {
-                if (h.getValue().split(" ")[0].equals("111")) {
-                    foundWarning = true;
-                }
+        Assert.assertEquals(200, result.getCode());
+        boolean foundWarning = false;
+        for (final Header h : result.getHeaders("Warning")) {
+            if (h.getValue().split(" ")[0].equals("111")) {
+                foundWarning = true;
             }
-            Assert.assertTrue(foundWarning);
-        } else {
-            Assert.assertTrue(status >= 500 && status <= 599);
         }
+        Assert.assertTrue(foundWarning);
     }
 
     /*
@@ -2326,15 +2001,11 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp2.setHeader("ETag", "\"etag\"");
         resp2.setHeader("Via", "1.1 fred");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        eqRequest(validate),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(validate), Mockito.any())).thenReturn(resp2);
 
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
 
-        replayMocks();
 
         final ClassicHttpResponse stale = execute(req1);
         Assert.assertNotNull(stale.getFirstHeader("Warning"));
@@ -2342,8 +2013,6 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpResponse result1 = execute(req2);
         final ClassicHttpResponse result2 = execute(req3);
 
-        verifyMocks();
-
         boolean found1xxWarning = false;
         final Iterator<HeaderElement> it = MessageSupport.iterate(result1, HttpHeaders.WARNING);
         while (it.hasNext()) {
@@ -2395,14 +2064,8 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
 
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        eqRequest(validate),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2);
-
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(validate), Mockito.any())).thenReturn(resp2);
 
         final ClassicHttpResponse stale = execute(req1);
         Assert.assertNotNull(stale.getFirstHeader("Warning"));
@@ -2410,8 +2073,6 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpResponse result1 = execute(req2);
         final ClassicHttpResponse result2 = execute(req3);
 
-        verifyMocks();
-
         boolean found214Warning = false;
         final Iterator<HeaderElement> it = MessageSupport.iterate(result1, HttpHeaders.WARNING);
         while (it.hasNext()) {
@@ -2464,11 +2125,9 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         impl = new CachingExec(mockCache, null, config);
         request = new BasicClassicHttpRequest("GET", "/thing");
 
-        EasyMock.expect(mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request))).andReturn(entry);
+        Mockito.when(mockCache.getCacheEntry(Mockito.eq(host), RequestEquivalent.eq(request))).thenReturn(entry);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
 
         Assert.assertEquals(200, result.getCode());
         Assert.assertEquals("11", result.getFirstHeader("Age").getValue());
@@ -2523,34 +2182,24 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
 
         final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry();
 
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
-
-        mockCache.flushCacheEntriesInvalidatedByExchange(
-                EasyMock.isA(HttpHost.class),
-                EasyMock.isA(ClassicHttpRequest.class),
-                EasyMock.isA(ClassicHttpResponse.class));
-        EasyMock.expect(mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request))).andReturn(entry);
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(cap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(validated).times(0, 1);
-        EasyMock.expect(mockCache.getCacheEntry(
-                EasyMock.isA(HttpHost.class),
-                EasyMock.isA(ClassicHttpRequest.class))).andReturn(entry).times(0, 1);
-        EasyMock.expect(mockCache.createCacheEntry(
-                EasyMock.isA(HttpHost.class),
-                EasyMock.isA(ClassicHttpRequest.class),
-                eqCloseableResponse(validated),
-                EasyMock.isA(ByteArrayBuffer.class),
-                EasyMock.isA(Date.class),
-                EasyMock.isA(Date.class))).andReturn(cacheEntry).times(0, 1);
-
-        replayMocks();
+        Mockito.when(mockCache.getCacheEntry(Mockito.eq(host), RequestEquivalent.eq(request))).thenReturn(entry);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(validated);
+        Mockito.when(mockCache.createCacheEntry(
+                Mockito.any(),
+                Mockito.any(),
+                ResponseEquivalent.eq(validated),
+                Mockito.any(),
+                Mockito.any(),
+                Mockito.any())).thenReturn(cacheEntry);
+
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
 
         Assert.assertEquals(200, result.getCode());
-        if (!cap.hasCaptured()) {
+
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain, Mockito.atMostOnce()).proceed(reqCapture.capture(), Mockito.any());
+        final List<ClassicHttpRequest> allRequests = reqCapture.getAllValues();
+        if (allRequests.isEmpty()) {
             // heuristic cache hit
             boolean found113Warning = false;
             final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.WARNING);
@@ -2564,6 +2213,13 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
             }
             Assert.assertTrue(found113Warning);
         }
+        Mockito.verify(mockCache).createCacheEntry(
+                Mockito.any(),
+                Mockito.any(),
+                Mockito.any(),
+                Mockito.any(),
+                Mockito.any(),
+                Mockito.any());
     }
 
     /*
@@ -2600,18 +2256,14 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
 
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        eqRequest(req1),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1);
-
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         execute(req2);
         final ClassicHttpResponse result = execute(req3);
-        verifyMocks();
         Assert.assertEquals("\"etag1\"", result.getFirstHeader("ETag").getValue());
     }
 
@@ -2630,26 +2282,22 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
      * from upstream.
      */
     private ClassicHttpResponse testRequestWithWeakETagValidatorIsNotAllowed(final String header) throws Exception {
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(cap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse).times(0, 1);
-
-        replayMocks();
         final ClassicHttpResponse response = execute(request);
-        verifyMocks();
 
         // it's probably ok to return a 400 (Bad Request) to this client
-        if (cap.hasCaptured()) {
-            final ClassicHttpRequest forwarded = cap.getValue();
-            final Header h = forwarded.getFirstHeader(header);
-            if (h != null) {
-                Assert.assertFalse(h.getValue().startsWith("W/"));
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain, Mockito.atMostOnce()).proceed(reqCapture.capture(), Mockito.any());
+        final List<ClassicHttpRequest> allRequests = reqCapture.getAllValues();
+        if (!allRequests.isEmpty()) {
+            final ClassicHttpRequest forwarded = reqCapture.getValue();
+            if (forwarded != null) {
+                final Header h = forwarded.getFirstHeader(header);
+                if (h != null) {
+                    Assert.assertFalse(h.getValue().startsWith("W/"));
+                }
             }
         }
         return response;
-
     }
 
     @Test
@@ -2725,17 +2373,14 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         req2.setHeader("Range", "bytes=0-50");
         req2.setHeader("If-Range", "W/\"etag\"");
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1).times(1, 2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         Assert.assertFalse(HttpStatus.SC_PARTIAL_CONTENT == result.getCode());
+
+        Mockito.verify(mockExecChain).proceed(Mockito.any(), Mockito.any());
     }
 
     /*
@@ -2761,23 +2406,17 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("Cache-Control", "max-age=0,max-stale=0");
 
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1);
-
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(cap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
         execute(req2);
-        verifyMocks();
 
-        final ClassicHttpRequest validation = cap.getValue();
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain, Mockito.times(2)).proceed(reqCapture.capture(), Mockito.any());
+
+        final List<ClassicHttpRequest> allRequests = reqCapture.getAllValues();
+        Assert.assertEquals(2, allRequests.size());
+        final ClassicHttpRequest validation = allRequests.get(1);
         boolean isConditional = false;
         final String[] conditionalHeaders = { "If-Range", "If-Modified-Since", "If-Unmodified-Since",
                 "If-Match", "If-None-Match" };
@@ -2836,17 +2475,13 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         req2.setHeader("If-Modified-Since", DateUtils.formatDate(twentySecondsAgo));
 
         // must hit the origin again for the second request
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1).times(2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         Assert.assertFalse(HttpStatus.SC_NOT_MODIFIED == result.getCode());
+        Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any());
     }
 
     @Test
@@ -2866,15 +2501,13 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         req2.setHeader("If-Modified-Since", DateUtils.formatDate(tenSecondsAgo));
 
         // may hit the origin again for the second request
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.isA(ClassicHttpRequest.class),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp1).times(1,2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
         execute(req2);
-        verifyMocks();
+
+        Mockito.verify(mockExecChain, Mockito.atLeastOnce()).proceed(Mockito.any(), Mockito.any());
+        Mockito.verify(mockExecChain, Mockito.atMost(2)).proceed(Mockito.any(), Mockito.any());
     }
 
 
@@ -2887,9 +2520,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
     @Test
     public void testCacheWithoutSupportForRangeAndContentRangeHeadersDoesNotCacheA206Response() throws Exception {
 
-        if (!supportsRangeAndContentRangeHeaders(impl)) {
-            emptyMockCacheExpectsNoPuts();
-
+        if (!impl.supportsRangeAndContentRangeHeaders()) {
             final ClassicHttpRequest req = new BasicClassicHttpRequest("GET", "/");
             req.setHeader("Range", "bytes=0-50");
 
@@ -2898,13 +2529,11 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
             resp.setHeader("ETag", "\"etag\"");
             resp.setHeader("Cache-Control", "max-age=3600");
 
-            EasyMock.expect(mockExecChain.proceed(
-                    EasyMock.isA(ClassicHttpRequest.class),
-                    EasyMock.isA(ExecChain.Scope.class))).andReturn(resp);
+            Mockito.when(mockExecChain.proceed(Mockito.any(),Mockito.any())).thenReturn(resp);
 
-            replayMocks();
             execute(req);
-            verifyMocks();
+
+            Mockito.verifyNoInteractions(mockCache);
         }
     }
 
@@ -2926,12 +2555,12 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse.removeHeaders("Expires");
         originResponse.removeHeaders("Cache-Control");
 
-        backendExpectsAnyRequest().andReturn(originResponse).times(2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         execute(request);
         execute(request);
-        verifyMocks();
+
+        Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any());
     }
 
     /*
@@ -2943,11 +2572,9 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse = HttpTestUtils.make200Response();
         originResponse.setHeader(header, value);
 
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
 
         Assert.assertEquals(value, result.getFirstHeader(header).getValue());
     }
@@ -2978,11 +2605,9 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
     private void testDoesNotAddHeaderToOriginResponse(final String header) throws Exception {
         originResponse.removeHeaders(header);
 
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
 
         Assert.assertNull(result.getFirstHeader(header));
     }
@@ -3016,12 +2641,10 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse.setHeader("Cache-Control", "max-age=3600");
         originResponse.setHeader(header, value);
 
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         Assert.assertEquals(value, result.getFirstHeader(header).getValue());
     }
@@ -3056,12 +2679,10 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse.addHeader("Cache-Control", "max-age=3600");
         originResponse.removeHeaders(header);
 
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         Assert.assertNull(result.getFirstHeader(header));
     }
@@ -3092,18 +2713,12 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         req.setHeader("Content-Length","128");
         req.setHeader(header,value);
 
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
-
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(cap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-
-        replayMocks();
         execute(req);
-        verifyMocks();
 
-        final ClassicHttpRequest captured = cap.getValue();
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any());
+
+        final ClassicHttpRequest captured = reqCapture.getValue();
         Assert.assertEquals(value, captured.getFirstHeader(header).getValue());
     }
 
@@ -3136,18 +2751,14 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         req.setHeader("Content-Length","128");
         req.removeHeaders(header);
 
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(cap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
-
-        replayMocks();
         execute(req);
-        verifyMocks();
 
-        final ClassicHttpRequest captured = cap.getValue();
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any());
+
+        final ClassicHttpRequest captured = reqCapture.getValue();
         Assert.assertNull(captured.getFirstHeader(header));
     }
 
@@ -3195,11 +2806,9 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
     public void testExpiresHeaderMatchesDateIfAddedToOriginResponse() throws Exception {
         originResponse.removeHeaders("Expires");
 
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
 
         final Header expHdr = result.getFirstHeader("Expires");
         if (expHdr != null) {
@@ -3216,12 +2825,10 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse.setHeader("Cache-Control","max-age=3600");
         originResponse.removeHeaders("Expires");
 
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         final Header expHdr = result.getFirstHeader("Expires");
         if (expHdr != null) {
@@ -3240,11 +2847,9 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse.addHeader("Cache-Control","no-transform");
         originResponse.setHeader(header, value);
 
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
 
         Assert.assertEquals(value, result.getFirstHeader(header).getValue());
     }
@@ -3276,12 +2881,10 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse.addHeader("Cache-Control","max-age=3600, no-transform");
         originResponse.setHeader(header, value);
 
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         Assert.assertEquals(value, result.getFirstHeader(header).getValue());
     }
@@ -3308,15 +2911,15 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse.addHeader("Cache-Control","max-age=3600, no-transform");
         originResponse.setHeader("Content-Range", "bytes 0-49/128");
 
-        backendExpectsAnyRequest().andReturn(originResponse).times(1,2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         Assert.assertEquals("bytes 0-49/128",
                             result.getFirstHeader("Content-Range").getValue());
+
+        Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any());
     }
 
     @Test
@@ -3408,13 +3011,13 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         req2.setHeader("Cache-Control","max-age=0, max-stale=0");
         final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         final InputStream i1 = resp1.getEntity().getContent();
         final InputStream i2 = result.getEntity().getContent();
@@ -3469,13 +3072,12 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp2.setHeader("Date", DateUtils.formatDate(new Date()));
         resp2.setHeader("Server", "MockServer/1.0");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(req2), Mockito.any())).thenReturn(resp2);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         final String[] endToEndHeaders = {
             "Cache-Control", "ETag", "Allow", "Content-Encoding",
@@ -3513,16 +3115,15 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp2.setHeader("Pragma","x-new-pragma");
         resp2.setHeader("Retry-After","120");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp2);
-
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
 
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
+
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
         final ClassicHttpResponse result1 = execute(req2);
         final ClassicHttpResponse result2 = execute(req3);
-        verifyMocks();
 
         final String[] endToEndHeaders = {
             "Date", "Cache-Control", "Allow", "Content-Language",
@@ -3554,16 +3155,16 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified");
         resp2.setHeader("Cache-Control", "max-age=1800");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp2);
-
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
 
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
+
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result1 = execute(req2);
         final ClassicHttpResponse result2 = execute(req3);
-        verifyMocks();
 
         final String h = "Cache-Control";
         Assert.assertEquals(HttpTestUtils.getCanonicalHeaderValue(resp2, h),
@@ -3609,7 +3210,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp1.setHeader("Content-Range","bytes 0-49/128");
         resp1.setHeader("ETag","\"etag1\"");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("Range","bytes=50-127");
@@ -3621,7 +3222,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp2.setHeader("Server","MockServer/1.0");
         resp2.setHeader("Date", DateUtils.formatDate(oneSecondAgo));
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
 
@@ -3630,13 +3231,11 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp3.setHeader("Server","MockServer/1.0");
         resp3.setHeader("Date", DateUtils.formatDate(now));
 
-        backendExpectsAnyRequestAndReturn(resp3);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
 
-        replayMocks();
         execute(req1);
         execute(req2);
         execute(req3);
-        verifyMocks();
     }
 
     @Test
@@ -3656,7 +3255,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp1.setHeader("Server","MockServer/1.0");
         resp1.setHeader("Date", DateUtils.formatDate(twoSecondsAgo));
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("Range","bytes=50-127");
@@ -3669,7 +3268,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp2.setHeader("Server","MockServer/1.0");
         resp2.setHeader("Date", DateUtils.formatDate(oneSecondAgo));
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
 
@@ -3678,13 +3277,11 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp3.setHeader("Server","MockServer/1.0");
         resp3.setHeader("Date", DateUtils.formatDate(now));
 
-        backendExpectsAnyRequestAndReturn(resp3);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
 
-        replayMocks();
         execute(req1);
         execute(req2);
         execute(req3);
-        verifyMocks();
     }
 
     @Test
@@ -3705,7 +3302,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp1.setHeader("Server","MockServer/1.0");
         resp1.setHeader("Date", DateUtils.formatDate(twoSecondsAgo));
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("Range","bytes=50-127");
@@ -3718,7 +3315,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp2.setHeader("Server","MockServer/1.0");
         resp2.setHeader("Date", DateUtils.formatDate(oneSecondAgo));
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
 
@@ -3727,13 +3324,11 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp3.setHeader("Server","MockServer/1.0");
         resp3.setHeader("Date", DateUtils.formatDate(now));
 
-        backendExpectsAnyRequestAndReturn(resp3);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
 
-        replayMocks();
         execute(req1);
         execute(req2);
         execute(req3);
-        verifyMocks();
     }
 
     @Test
@@ -3754,7 +3349,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp1.setHeader("Server","MockServer/1.0");
         resp1.setHeader("Date", DateUtils.formatDate(twoSecondsAgo));
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("Range","bytes=50-127");
@@ -3766,7 +3361,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp2.setHeader("Server","MockServer/1.0");
         resp2.setHeader("Date", DateUtils.formatDate(oneSecondAgo));
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
         req3.setHeader("Range","bytes=0-49");
@@ -3777,13 +3372,11 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp3.setHeader("Date", DateUtils.formatDate(now));
 
         // must make this request; cannot serve from cache
-        backendExpectsAnyRequestAndReturn(resp3);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
 
-        replayMocks();
         execute(req1);
         execute(req2);
         execute(req3);
-        verifyMocks();
     }
 
     @Test
@@ -3803,7 +3396,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp1.setHeader("Server","MockServer/1.0");
         resp1.setHeader("Date", DateUtils.formatDate(twoSecondsAgo));
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("Range","bytes=50-127");
@@ -3816,7 +3409,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp2.setHeader("Server","MockServer/1.0");
         resp2.setHeader("Date", DateUtils.formatDate(oneSecondAgo));
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
         req3.setHeader("Range","bytes=0-49");
@@ -3827,13 +3420,11 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp3.setHeader("Date", DateUtils.formatDate(now));
 
         // must make this request; cannot serve from cache
-        backendExpectsAnyRequestAndReturn(resp3);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
 
-        replayMocks();
         execute(req1);
         execute(req2);
         execute(req3);
-        verifyMocks();
     }
 
     @Test
@@ -3854,7 +3445,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp1.setHeader("Server","MockServer/1.0");
         resp1.setHeader("Date", DateUtils.formatDate(twoSecondsAgo));
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("Range","bytes=50-127");
@@ -3867,7 +3458,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp2.setHeader("Server","MockServer/1.0");
         resp2.setHeader("Date", DateUtils.formatDate(oneSecondAgo));
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
         req3.setHeader("Range","bytes=0-49");
@@ -3878,13 +3469,11 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp3.setHeader("Date", DateUtils.formatDate(now));
 
         // must make this request; cannot serve from cache
-        backendExpectsAnyRequestAndReturn(resp3);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
 
-        replayMocks();
         execute(req1);
         execute(req2);
         execute(req3);
-        verifyMocks();
     }
 
     @Test
@@ -3905,7 +3494,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp1.setHeader("Server","MockServer/1.0");
         resp1.setHeader("Date", DateUtils.formatDate(oneSecondAgo));
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("Range","bytes=50-127");
@@ -3918,7 +3507,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp2.setHeader("Server","MockServer/1.0");
         resp2.setHeader("Date", DateUtils.formatDate(twoSecondsAgo));
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
         req3.setHeader("Range","bytes=50-127");
@@ -3929,13 +3518,11 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp3.setHeader("Date", DateUtils.formatDate(now));
 
         // must make this request; cannot serve from cache
-        backendExpectsAnyRequestAndReturn(resp3);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
 
-        replayMocks();
         execute(req1);
         execute(req2);
         execute(req3);
-        verifyMocks();
     }
 
     @Test
@@ -3955,7 +3542,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp1.setHeader("Server","MockServer/1.0");
         resp1.setHeader("Date", DateUtils.formatDate(oneSecondAgo));
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("Range","bytes=50-127");
@@ -3968,7 +3555,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp2.setHeader("Server","MockServer/1.0");
         resp2.setHeader("Date", DateUtils.formatDate(oneSecondAgo));
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
         req3.setHeader("Range","bytes=0-49");
@@ -3979,13 +3566,11 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp3.setHeader("Date", DateUtils.formatDate(now));
 
         // must make this request; cannot serve from cache
-        backendExpectsAnyRequestAndReturn(resp3);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
 
-        replayMocks();
         execute(req1);
         execute(req2);
         execute(req3);
-        verifyMocks();
     }
 
     /* "When the cache receives a subsequent request whose Request-URI
@@ -4008,7 +3593,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp1.setHeader("Cache-Control","max-age=3600");
         resp1.setHeader("Vary","Accept-Encoding");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.removeHeaders("Accept-Encoding");
@@ -4018,12 +3603,10 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp2.setHeader("Cache-Control","max-age=3600");
 
         // not allowed to have a cache hit; must forward request
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
-        replayMocks();
         execute(req1);
         execute(req2);
-        verifyMocks();
     }
 
     /* "A Vary header field-value of "*" always fails to match and
@@ -4041,7 +3624,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp1.setHeader("Cache-Control","max-age=3600");
         resp1.setHeader("Vary","*");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
 
@@ -4050,12 +3633,10 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp2.setHeader("Cache-Control","max-age=3600");
 
         // not allowed to have a cache hit; must forward request
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
-        replayMocks();
         execute(req1);
         execute(req2);
-        verifyMocks();
     }
 
     /* " If the selecting request header fields for the cached entry
@@ -4092,47 +3673,26 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp1.setHeader("Vary","User-Agent");
         resp1.setHeader("Content-Type","application/octet-stream");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("User-Agent","MyBrowser/1.5");
 
-        final ClassicHttpRequest conditional = new BasicClassicHttpRequest("GET", "/");
-        conditional.setHeader("User-Agent","MyBrowser/1.5");
-        conditional.setHeader("If-None-Match","\"etag1\"");
-
         final ClassicHttpResponse resp200 = HttpTestUtils.make200Response();
         resp200.setHeader("ETag","\"etag1\"");
         resp200.setHeader("Vary","User-Agent");
 
-        final ClassicHttpResponse resp304 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified");
-        resp304.setHeader("ETag","\"etag1\"");
-        resp304.setHeader("Vary","User-Agent");
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        final Capture<ClassicHttpRequest> condCap = EasyMock.newCapture();
-        final Capture<ClassicHttpRequest> uncondCap = EasyMock.newCapture();
+        execute(req1);
 
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.and(eqRequest(conditional), EasyMock.capture(condCap)),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp304).times(0,1);
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.and(eqRequest(req2), EasyMock.capture(uncondCap)),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp200).times(0,1);
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(req2), Mockito.any())).thenReturn(resp200);
 
-        replayMocks();
-        execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
-        if (HttpStatus.SC_OK == result.getCode()) {
-            Assert.assertTrue(condCap.hasCaptured()
-                              || uncondCap.hasCaptured());
-            if (uncondCap.hasCaptured()) {
-                Assert.assertTrue(HttpTestUtils.semanticallyTransparent(resp200, result));
-            }
-        }
+        Assert.assertEquals(HttpStatus.SC_OK, result.getCode());
+
+        Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any());
+
+        Assert.assertTrue(HttpTestUtils.semanticallyTransparent(resp200, result));
     }
 
     /* "Some HTTP methods MUST cause a cache to invalidate an
@@ -4151,24 +3711,30 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
         resp1.setHeader("Cache-Control","public, max-age=3600");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content");
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
         final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
         resp3.setHeader("Cache-Control","public, max-age=3600");
 
         // this origin request MUST happen due to invalidation
-        backendExpectsAnyRequestAndReturn(resp3);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
 
-        replayMocks();
         execute(req1);
         execute(unsafeReq);
         execute(req3);
-        verifyMocks();
+    }
+
+    protected ClassicHttpRequest makeRequestWithBody(final String method, final String requestUri) {
+        final ClassicHttpRequest req = new BasicClassicHttpRequest(method, requestUri);
+        final int nbytes = 128;
+        req.setEntity(HttpTestUtils.makeBody(nbytes));
+        req.setHeader("Content-Length", Long.toString(nbytes));
+        return req;
     }
 
     @Test
@@ -4195,24 +3761,22 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
         resp1.setHeader("Cache-Control","public, max-age=3600");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content");
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/content");
         final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
         resp3.setHeader("Cache-Control","public, max-age=3600");
 
         // this origin request MUST happen due to invalidation
-        backendExpectsAnyRequestAndReturn(resp3);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
 
-        replayMocks();
         execute(req1);
         execute(unsafeReq);
         execute(req3);
-        verifyMocks();
     }
 
     protected void testUnsafeMethodInvalidatesCacheForUriInContentLocationHeader(
@@ -4302,19 +3866,18 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
         resp1.setHeader("Cache-Control","public, max-age=3600");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content");
 
-        backendExpectsAnyRequestAndReturn(resp2);
-
         final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/content");
 
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
+
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         execute(unsafeReq);
         execute(req3);
-        verifyMocks();
     }
 
     protected void testUnsafeMethodDoesNotInvalidateCacheForUriInContentLocationHeadersFromOtherHosts(
@@ -4329,15 +3892,6 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         testUnsafeMethodDoesNotInvalidateCacheForHeaderUri(unsafeReq);
     }
 
-    protected ClassicHttpRequest makeRequestWithBody(final String method, final String requestUri) {
-        final ClassicHttpRequest req =
-            new BasicClassicHttpRequest(method, requestUri);
-        final int nbytes = 128;
-        req.setEntity(HttpTestUtils.makeBody(nbytes));
-        req.setHeader("Content-Length",""+nbytes);
-        return req;
-    }
-
     @Test
     public void testPutDoesNotInvalidateCacheForUriInContentLocationHeadersFromOtherHosts() throws Exception {
         final ClassicHttpRequest req = makeRequestWithBody("PUT","/");
@@ -4386,17 +3940,12 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
     private void testRequestIsWrittenThroughToOrigin(final ClassicHttpRequest req) throws Exception {
         final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content");
         final ClassicHttpRequest wrapper = req;
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        eqRequest(wrapper),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp);
+        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(wrapper), Mockito.any())).thenReturn(resp);
 
-        replayMocks();
         execute(wrapper);
-        verifyMocks();
     }
 
-    @Test @Ignore
+    @Test
     public void testOPTIONSRequestsAreWrittenThroughToOrigin() throws Exception {
         final ClassicHttpRequest req = new BasicClassicHttpRequest("OPTIONS","*");
         testRequestIsWrittenThroughToOrigin(req);
@@ -4454,11 +4003,9 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final String reallyOldAge = "1" + Long.MAX_VALUE;
         originResponse.setHeader("Age",reallyOldAge);
 
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
 
         Assert.assertEquals("2147483648",
                             result.getFirstHeader("Age").getValue());
@@ -4474,10 +4021,8 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
     public void testDoesNotModifyAllowHeaderWithUnknownMethods() throws Exception {
         final String allowHeaderValue = "GET, HEAD, FOOBAR";
         originResponse.setHeader("Allow",allowHeaderValue);
-        backendExpectsAnyRequest().andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
         Assert.assertEquals(HttpTestUtils.getCanonicalHeaderValue(originResponse,"Allow"),
                             HttpTestUtils.getCanonicalHeaderValue(result, "Allow"));
     }
@@ -4514,21 +4059,20 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
             final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
             req1.setHeader("Authorization",authorization);
 
-            backendExpectsAnyRequestAndReturn(authorizedResponse);
-
             final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
             final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
             resp2.setHeader("Cache-Control","max-age=3600");
 
-            if (maxTimes > 0) {
-                // this request MUST happen
-                backendExpectsAnyRequest().andReturn(resp2).times(minTimes,maxTimes);
-            }
+            Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(authorizedResponse);
 
-            replayMocks();
             execute(req1);
+
+            Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
             execute(req2);
-            verifyMocks();
+
+            Mockito.verify(mockExecChain, Mockito.atLeast(1 + minTimes)).proceed(Mockito.any(), Mockito.any());
+            Mockito.verify(mockExecChain, Mockito.atMost(1 + maxTimes)).proceed(Mockito.any(), Mockito.any());
         }
     }
 
@@ -4580,25 +4124,26 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
             final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
             req1.setHeader("Authorization",authorization1);
 
-            backendExpectsAnyRequestAndReturn(authorizedResponse);
-
             final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
             req2.setHeader("Authorization",authorization2);
 
             final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
 
-            final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
-            EasyMock.expect(
-                    mockExecChain.proceed(
-                            EasyMock.capture(cap),
-                            EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2);
+            Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(authorizedResponse);
 
-            replayMocks();
             execute(req1);
+
+            Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
             execute(req2);
-            verifyMocks();
 
-            final ClassicHttpRequest captured = cap.getValue();
+            final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+            Mockito.verify(mockExecChain, Mockito.times(2)).proceed(reqCapture.capture(), Mockito.any());
+
+            final List<ClassicHttpRequest> allRequests = reqCapture.getAllValues();
+            Assert.assertEquals(2, allRequests.size());
+
+            final ClassicHttpRequest captured = allRequests.get(1);
             Assert.assertEquals(HttpTestUtils.getCanonicalHeaderValue(req2, "Authorization"),
                     HttpTestUtils.getCanonicalHeaderValue(captured, "Authorization"));
         }
@@ -4651,24 +4196,20 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp1.setHeader("Cache-Control","max-age=5");
         resp1.setHeader("Etag","\"etag\"");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("Cache-Control","max-stale=60");
-        final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
 
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(cap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2).times(0,1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
-        if (!cap.hasCaptured()) {
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain, Mockito.atMostOnce()).proceed(reqCapture.capture(), Mockito.any());
+
+        final List<ClassicHttpRequest> allRequests = reqCapture.getAllValues();
+        if (allRequests.isEmpty()) {
             boolean found110Warning = false;
             final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.WARNING);
             while (it.hasNext()) {
@@ -4691,20 +4232,19 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
     @Test
     public void testDoesNotTransmitNoCacheDirectivesWithFieldsDownstream() throws Exception {
         request.setHeader("Cache-Control","no-cache=\"X-Field\"");
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
-        EasyMock.expect(mockExecChain.proceed(
-                EasyMock.capture(cap),
-                EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse).times(0,1);
 
-        replayMocks();
         try {
             execute(request);
         } catch (final ClientProtocolException acceptable) {
         }
-        verifyMocks();
 
-        if (cap.hasCaptured()) {
-            final ClassicHttpRequest captured = cap.getValue();
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain, Mockito.atMostOnce()).proceed(reqCapture.capture(), Mockito.any());
+
+        final List<ClassicHttpRequest> allRequests = reqCapture.getAllValues();
+
+        if (!allRequests.isEmpty()) {
+            final ClassicHttpRequest captured = reqCapture.getValue();
             final Iterator<HeaderElement> it = MessageSupport.iterate(captured, HttpHeaders.CACHE_CONTROL);
             while (it.hasNext()) {
                 final HeaderElement elt = it.next();
@@ -4727,24 +4267,24 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp1.setHeader("Etag","\"etag\"");
         resp1.setHeader("Cache-Control","max-age=3600");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
+
+        execute(req1);
 
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
         resp2.setHeader("Etag","\"etag2\"");
         resp2.setHeader("Cache-Control","max-age=1200");
 
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
-        EasyMock.expect(mockExecChain.proceed(
-                EasyMock.capture(cap),
-                EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
-        replayMocks();
-        execute(req1);
         final ClassicHttpResponse result = execute(req);
-        verifyMocks();
 
         Assert.assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result));
-        final ClassicHttpRequest captured = cap.getValue();
+
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain, Mockito.times(2)).proceed(reqCapture.capture(), Mockito.any());
+
+        final ClassicHttpRequest captured = reqCapture.getValue();
         Assert.assertTrue(HttpTestUtils.equivalent(req, captured));
     }
 
@@ -4775,27 +4315,25 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
             final ClassicHttpResponse staleResponse) throws Exception {
         final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
 
-        backendExpectsAnyRequestAndReturn(staleResponse);
-
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("Cache-Control","max-stale=3600");
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
         resp2.setHeader("ETag","\"etag2\"");
         resp2.setHeader("Cache-Control","max-age=5, must-revalidate");
 
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
         // this request MUST happen
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(cap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(staleResponse);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         execute(req2);
-        verifyMocks();
 
-        final ClassicHttpRequest reval = cap.getValue();
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain, Mockito.times(2)).proceed(reqCapture.capture(), Mockito.any());
+
+        final ClassicHttpRequest reval = reqCapture.getValue();
         boolean foundMaxAge0 = false;
         final Iterator<HeaderElement> it = MessageSupport.iterate(reval, HttpHeaders.CACHE_CONTROL);
         while (it.hasNext()) {
@@ -4829,16 +4367,15 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
             final ClassicHttpResponse staleResponse) throws Exception {
         final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
 
-        backendExpectsAnyRequestAndReturn(staleResponse);
-
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
 
-        backendExpectsAnyRequest().andThrow(new SocketTimeoutException());
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(staleResponse);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenThrow(new SocketTimeoutException());
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         Assert.assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT,
                             result.getCode());
@@ -4898,47 +4435,46 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
      */
     @Test
     public void testCacheControlPrivateIsNotCacheableBySharedCache() throws Exception {
-       if (config.isSharedCache()) {
-               final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
-               final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
-               resp1.setHeader("Cache-Control","private,max-age=3600");
+        if (config.isSharedCache()) {
+            final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
+            final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
+            resp1.setHeader("Cache-Control", "private,max-age=3600");
 
-               backendExpectsAnyRequestAndReturn(resp1);
+            Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-               final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
-               final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
-               // this backend request MUST happen
-               backendExpectsAnyRequestAndReturn(resp2);
+            final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
+            final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
+            // this backend request MUST happen
+            Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
-               replayMocks();
-               execute(req1);
-               execute(req2);
-               verifyMocks();
-       }
+            execute(req1);
+            execute(req2);
+        }
     }
 
     @Test
     public void testCacheControlPrivateOnFieldIsNotReturnedBySharedCache() throws Exception {
-       if (config.isSharedCache()) {
-               final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
-               final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
-               resp1.setHeader("X-Personal","stuff");
-               resp1.setHeader("Cache-Control","private=\"X-Personal\",s-maxage=3600");
+        if (config.isSharedCache()) {
+            final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
+            final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
+            resp1.setHeader("X-Personal", "stuff");
+            resp1.setHeader("Cache-Control", "private=\"X-Personal\",s-maxage=3600");
 
-               backendExpectsAnyRequestAndReturn(resp1);
+            Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
+
+            final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
+            final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
 
-               final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
-               final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
+            // this backend request MAY happen
+            Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
-               // this backend request MAY happen
-               backendExpectsAnyRequestAndReturn(resp2).times(0,1);
+            execute(req1);
+            final ClassicHttpResponse result = execute(req2);
+            Assert.assertNull(result.getFirstHeader("X-Personal"));
 
-               replayMocks();
-               execute(req1);
-               final ClassicHttpResponse result = execute(req2);
-               verifyMocks();
-               Assert.assertNull(result.getFirstHeader("X-Personal"));
-       }
+            Mockito.verify(mockExecChain, Mockito.atLeastOnce()).proceed(Mockito.any(), Mockito.any());
+            Mockito.verify(mockExecChain, Mockito.atMost(2)).proceed(Mockito.any(), Mockito.any());
+        }
     }
 
     /* "If the no-cache directive does not specify a field-name, then a
@@ -4956,18 +4492,16 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp1.setHeader("ETag","\"etag\"");
         resp1.setHeader("Cache-Control","no-cache");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
 
         // this MUST happen
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
-        replayMocks();
         execute(req1);
         execute(req2);
-        verifyMocks();
     }
 
     @Test
@@ -4977,19 +4511,17 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp1.setHeader("ETag","\"etag\"");
         resp1.setHeader("Cache-Control","no-cache,s-maxage=3600");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("Cache-Control","max-stale=7200");
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
 
         // this MUST happen
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
-        replayMocks();
         execute(req1);
         execute(req2);
-        verifyMocks();
     }
 
     /* "If the no-cache directive does specify one or more field-names, then
@@ -5006,7 +4538,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp1.setHeader("X-Stuff","things");
         resp1.setHeader("Cache-Control","no-cache=\"X-Stuff\", max-age=3600");
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
@@ -5014,18 +4546,16 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp2.setHeader("X-Stuff","things");
         resp2.setHeader("Cache-Control","no-cache=\"X-Stuff\",max-age=3600");
 
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(cap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2).times(0,1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
-        if (!cap.hasCaptured()) {
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain, Mockito.atMost(2)).proceed(reqCapture.capture(), Mockito.any());
+
+        final List<ClassicHttpRequest> allRequests = reqCapture.getAllValues();
+        if (allRequests.isEmpty()) {
             Assert.assertNull(result.getFirstHeader("X-Stuff"));
         }
     }
@@ -5047,47 +4577,43 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
      */
     @Test
     public void testNoStoreOnRequestIsNotStoredInCache() throws Exception {
-        emptyMockCacheExpectsNoPuts();
         request.setHeader("Cache-Control","no-store");
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         execute(request);
-        verifyMocks();
+
+        Mockito.verifyNoInteractions(mockCache);
     }
 
     @Test
     public void testNoStoreOnRequestIsNotStoredInCacheEvenIfResponseMarkedCacheable() throws Exception {
-        emptyMockCacheExpectsNoPuts();
         request.setHeader("Cache-Control","no-store");
         originResponse.setHeader("Cache-Control","max-age=3600");
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         execute(request);
-        verifyMocks();
+
+        Mockito.verifyNoInteractions(mockCache);
     }
 
     @Test
     public void testNoStoreOnResponseIsNotStoredInCache() throws Exception {
-        emptyMockCacheExpectsNoPuts();
         originResponse.setHeader("Cache-Control","no-store");
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         execute(request);
-        verifyMocks();
+
+        Mockito.verifyNoInteractions(mockCache);
     }
 
     @Test
     public void testNoStoreOnResponseIsNotStoredInCacheEvenWithContraryIndicators() throws Exception {
-        emptyMockCacheExpectsNoPuts();
         originResponse.setHeader("Cache-Control","no-store,max-age=3600");
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         execute(request);
-        verifyMocks();
+
+        Mockito.verifyNoInteractions(mockCache);
     }
 
     /* "If multiple encodings have been applied to an entity, the content
@@ -5099,11 +4625,9 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
     public void testOrderOfMultipleContentEncodingHeaderValuesIsPreserved() throws Exception {
         originResponse.addHeader("Content-Encoding","gzip");
         originResponse.addHeader("Content-Encoding","deflate");
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
         int total_encodings = 0;
         final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.CONTENT_ENCODING);
         while (it.hasNext()) {
@@ -5126,11 +4650,9 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
     @Test
     public void testOrderOfMultipleParametersInContentEncodingHeaderIsPreserved() throws Exception {
         originResponse.addHeader("Content-Encoding","gzip,deflate");
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
         int total_encodings = 0;
         final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.CONTENT_ENCODING);
         while (it.hasNext()) {
@@ -5169,13 +4691,11 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         resp2.setHeader("Cache-Control","public,max-age=3600");
         resp2.setHeader("Etag","\"etag\"");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
-        replayMocks();
         execute(req1);
         execute(req2);
-        verifyMocks();
     }
 
     /* "A received message that does not have a Date header field MUST be
@@ -5190,11 +4710,9 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse.setHeader("Cache-Control","public");
         originResponse.setHeader("ETag","\"etag\"");
 
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
         Assert.assertNotNull(result.getFirstHeader("Date"));
     }
 
@@ -5216,14 +4734,12 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
         // second request to origin MUST happen
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
-        replayMocks();
         execute(req1);
         execute(req2);
-        verifyMocks();
     }
 
     @Test
@@ -5252,14 +4768,12 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
 
-        backendExpectsAnyRequestAndReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
         // second request to origin MUST happen
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
-        replayMocks();
         execute(req1);
         execute(req2);
-        verifyMocks();
     }
 
     /* "If the response is being forwarded through a proxy, the proxy
@@ -5272,11 +4786,9 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final String server = "MockServer/1.0";
         originResponse.setHeader("Server", server);
 
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
         Assert.assertEquals(server, result.getFirstHeader("Server").getValue());
     }
 
@@ -5290,11 +4802,9 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse.addHeader("Transfer-Encoding","chunked");
         originResponse.addHeader("Transfer-Encoding","x-transfer");
 
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
         int transfer_encodings = 0;
         final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.TRANSFER_ENCODING);
         while (it.hasNext()) {
@@ -5318,11 +4828,9 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
     public void testOrderOfMultipleTransferEncodingsInSingleHeadersIsPreserved() throws Exception {
         originResponse.addHeader("Transfer-Encoding","chunked, x-transfer");
 
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
         int transfer_encodings = 0;
         final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.TRANSFER_ENCODING);
         while (it.hasNext()) {
@@ -5357,11 +4865,9 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse.setHeader("Vary","User-Agent");
         originResponse.setHeader("ETag","\"etag\"");
 
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
         final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.VARY);
         while (it.hasNext()) {
             final HeaderElement elt = it.next();
@@ -5378,18 +4884,15 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
      */
     @Test
     public void testProperlyFormattedViaHeaderIsAddedToRequests() throws Exception {
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
         request.removeHeaders("Via");
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(cap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         execute(request);
-        verifyMocks();
 
-        final ClassicHttpRequest captured = cap.getValue();
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any());
+
+        final ClassicHttpRequest captured = reqCapture.getValue();
         final String via = captured.getFirstHeader("Via").getValue();
         assertValidViaHeader(via);
     }
@@ -5397,10 +4900,8 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
     @Test
     public void testProperlyFormattedViaHeaderIsAddedToResponses() throws Exception {
         originResponse.removeHeaders("Via");
-        backendExpectsAnyRequest().andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
         assertValidViaHeader(result.getFirstHeader("Via").getValue());
     }
 
@@ -5479,17 +4980,15 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originalRequest.setVersion(HttpVersion.HTTP_1_0);
         request = originalRequest;
         request.removeHeaders("Via");
-        final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
-        EasyMock.expect(
-                mockExecChain.proceed(
-                        EasyMock.capture(cap),
-                        EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
 
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
+
         execute(request);
-        verifyMocks();
 
-        final ClassicHttpRequest captured = cap.getValue();
+        final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any());
+
+        final ClassicHttpRequest captured = reqCapture.getValue();
         final String via = captured.getFirstHeader("Via").getValue();
         final String protocol = via.split("\\s+")[0];
         final String[] protoParts = protocol.split("/");
@@ -5505,11 +5004,9 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content");
         originResponse.setVersion(HttpVersion.HTTP_1_0);
 
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
 
         final String via = result.getFirstHeader("Via").getValue();
         final String protocol = via.split("\\s+")[0];
@@ -5532,11 +5029,9 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse.removeHeaders("Warning");
         final String warning = "199 fred \"misc\"";
         originResponse.addHeader("Warning", warning);
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
         Assert.assertEquals(warning,
                 result.getFirstHeader("Warning").getValue());
     }
@@ -5569,13 +5064,11 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         final String newWarning = "113 betty \"stale too\"";
         resp2.setHeader("Warning", newWarning);
 
-        backendExpectsAnyRequestAndReturn(resp1);
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
 
-        replayMocks();
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         boolean oldWarningFound = false;
         boolean newWarningFound = false;
@@ -5607,10 +5100,8 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse.setVersion(HttpVersion.HTTP_1_0);
         originResponse.addHeader("Warning", origWarning);
         originResponse.setHeader("Date", dateHdr);
-        backendExpectsAnyRequest().andReturn(originResponse);
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
         // note that currently the implementation acts as an HTTP/1.1 proxy,
         // which means that all the responses from the caching module should
         // be HTTP/1.1, so we won't actually be testing anything here until
@@ -5649,11 +5140,9 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse.addHeader("Warning", "110 fred \"stale\", 110 wilma \"stale\" \""
                 + DateUtils.formatDate(tenSecondsAgo) + "\"");
         originResponse.setHeader("Cache-Control","no-cache,no-store");
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
 
         for(final Header h : result.getHeaders("Warning")) {
             Assert.assertFalse(h.getValue().contains("wilma"));
@@ -5668,11 +5157,9 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse.addHeader("Warning", "110 fred \"stale\", 110 wilma \"stale\" \""
                 + DateUtils.formatDate(tenSecondsAgo) + "\"");
         originResponse.setHeader("Cache-Control","public,max-age=3600");
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
 
         for(final Header h : result.getHeaders("Warning")) {
             Assert.assertFalse(h.getValue().contains("wilma"));
@@ -5686,11 +5173,9 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         originResponse.setHeader("Date", DateUtils.formatDate(now));
         originResponse.addHeader("Warning", "110 wilma \"stale\" \""
                 + DateUtils.formatDate(tenSecondsAgo) + "\"");
-        backendExpectsAnyRequest().andReturn(originResponse);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
 
-        replayMocks();
         final ClassicHttpResponse result = execute(request);
-        verifyMocks();
 
         final Header[] warningHeaders = result.getHeaders("Warning");
         Assert.assertTrue(warningHeaders == null || warningHeaders.length == 0);
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestRFC5861Compliance.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestRFC5861Compliance.java
index c1cd83b..c40fe4a 100644
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestRFC5861Compliance.java
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestRFC5861Compliance.java
@@ -31,37 +31,90 @@ import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.util.Date;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 
+import org.apache.hc.client5.http.HttpRoute;
+import org.apache.hc.client5.http.classic.ExecChain;
+import org.apache.hc.client5.http.classic.ExecRuntime;
 import org.apache.hc.client5.http.impl.schedule.ImmediateSchedulingStrategy;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
 import org.apache.hc.client5.http.utils.DateUtils;
 import org.apache.hc.core5.http.ClassicHttpRequest;
 import org.apache.hc.core5.http.ClassicHttpResponse;
 import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.HttpStatus;
 import org.apache.hc.core5.http.io.entity.InputStreamEntity;
+import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
 import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
-import org.easymock.EasyMock;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
 
 /**
  * A suite of acceptance tests for compliance with RFC5861, which
  * describes the stale-if-error and stale-while-revalidate
  * Cache-Control extensions.
  */
-public class TestRFC5861Compliance extends AbstractProtocolTest {
-
-    private ScheduledExecutorService executorService;
+@RunWith(MockitoJUnitRunner.class)
+public class TestRFC5861Compliance {
+
+    static final int MAX_BYTES = 1024;
+    static final int MAX_ENTRIES = 100;
+    static final int ENTITY_LENGTH = 128;
+
+    HttpHost host;
+    HttpRoute route;
+    HttpEntity body;
+    HttpClientContext context;
+    @Mock
+    ExecChain mockExecChain;
+    @Mock
+    ExecRuntime mockExecRuntime;
+    @Mock
+    HttpCache mockCache;
+    ClassicHttpRequest request;
+    ClassicHttpResponse originResponse;
+    CacheConfig config;
+    CachingExec impl;
+    HttpCache cache;
+    ScheduledExecutorService executorService;
 
     @Before
-    public void setup() {
+    public void setUp() throws Exception {
+        host = new HttpHost("foo.example.com", 80);
+
+        route = new HttpRoute(host);
+
+        body = HttpTestUtils.makeBody(ENTITY_LENGTH);
+
+        request = new BasicClassicHttpRequest("GET", "/foo");
+
+        context = HttpClientContext.create();
+
+        originResponse = HttpTestUtils.make200Response();
+
+        config = CacheConfig.custom()
+                .setMaxCacheEntries(MAX_ENTRIES)
+                .setMaxObjectSize(MAX_BYTES)
+                .build();
+
+        cache = new BasicHttpCache(config);
+        impl = new CachingExec(cache, null, config);
+
         executorService = new ScheduledThreadPoolExecutor(1);
-        EasyMock.expect(mockExecRuntime.fork(null)).andReturn(mockExecRuntime).anyTimes();
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
+        Mockito.when(mockExecRuntime.fork(null)).thenReturn(mockExecRuntime);
     }
 
     @After
@@ -69,16 +122,11 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         executorService.shutdownNow();
     }
 
-    @Override
-    protected void replayMocks() {
-        super.replayMocks();
-        EasyMock.replay(mockExecRuntime);
-    }
-
-    @Override
-    protected void verifyMocks() {
-        super.verifyMocks();
-        EasyMock.verify(mockExecRuntime);
+    public ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException {
+        return impl.execute(
+                ClassicRequestBuilder.copy(request).build(),
+                new ExecChain.Scope("test", route, request, mockExecRuntime, context),
+                mockExecChain);
     }
 
     /*
@@ -104,17 +152,16 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
                 "public, max-age=5, stale-if-error=60");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         HttpTestUtils.assert110WarningFound(result);
     }
@@ -127,8 +174,6 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
                 "public, max-age=5, stale-if-error=60");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
         final byte[] body101 = HttpTestUtils.getRandomBytes(101);
@@ -137,12 +182,13 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         final HttpEntity entity = new InputStreamEntity(cis, 101, null);
         resp2.setEntity(entity);
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         execute(req2);
-        verifyMocks();
 
         assertTrue(cis.wasClosed());
     }
@@ -155,17 +201,16 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
                 "public, max-age=5, stale-if-error=60, must-revalidate");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         assertTrue(HttpStatus.SC_OK != result.getCode());
     }
@@ -179,17 +224,16 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
                 "public, max-age=5, stale-if-error=60, proxy-revalidate");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         assertTrue(HttpStatus.SC_OK != result.getCode());
     }
@@ -206,17 +250,16 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
                 "public, max-age=5, stale-if-error=60, proxy-revalidate");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         HttpTestUtils.assert110WarningFound(result);
     }
@@ -229,18 +272,17 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
                 "public, max-age=5, stale-if-error=60");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control","min-fresh=2");
         final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         assertTrue(HttpStatus.SC_OK != result.getCode());
     }
@@ -253,18 +295,17 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
                 "public, max-age=5");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control","public, stale-if-error=60");
         final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         HttpTestUtils.assert110WarningFound(result);
     }
@@ -278,18 +319,17 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
         resp1.setHeader("Cache-Control", "public, max-age=5");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control", "public, stale-if-error=60");
         final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         HttpTestUtils.assert110WarningFound(result);
     }
@@ -303,17 +343,16 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
                 "public, max-age=5, stale-if-error=2");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR,
                 result.getCode());
@@ -328,21 +367,19 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
                 "public, max-age=5");
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control","stale-if-error=2");
         final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
-        assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR,
-                result.getCode());
+        assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, result.getCode());
     }
 
     /*
@@ -372,14 +409,12 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         resp1.setHeader("ETag","\"etag\"");
         resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
 
-        backendExpectsAnyRequestAndReturn(resp1).times(1,2);
-
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
 
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
+
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         assertEquals(HttpStatus.SC_OK, result.getCode());
         boolean warning110Found = false;
@@ -392,6 +427,9 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
             }
         }
         assertTrue(warning110Found);
+
+        Mockito.verify(mockExecChain, Mockito.atLeastOnce()).proceed(Mockito.any(), Mockito.any());
+        Mockito.verify(mockExecChain, Mockito.atMost(2)).proceed(Mockito.any(), Mockito.any());
     }
 
     @Test
@@ -409,14 +447,12 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         resp1.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15");
         resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
 
-        backendExpectsAnyRequestAndReturn(resp1).times(1, 2);
-
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
 
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
+
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         assertEquals(HttpStatus.SC_OK, result.getCode());
         boolean warning110Found = false;
@@ -429,6 +465,9 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
             }
         }
         assertTrue(warning110Found);
+
+        Mockito.verify(mockExecChain, Mockito.atLeastOnce()).proceed(Mockito.any(), Mockito.any());
+        Mockito.verify(mockExecChain, Mockito.atMost(2)).proceed(Mockito.any(), Mockito.any());
     }
 
     @Test
@@ -451,15 +490,13 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         resp1.setHeader("ETag","\"etag\"");
         resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
 
-        backendExpectsAnyRequestAndReturn(resp1).times(1,2);
-
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("If-None-Match","\"etag\"");
 
-        replayMocks();
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
+
         execute(req1);
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode());
         boolean warning110Found = false;
@@ -472,8 +509,10 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
             }
         }
         assertTrue(warning110Found);
-    }
 
+        Mockito.verify(mockExecChain, Mockito.atLeastOnce()).proceed(Mockito.any(), Mockito.any());
+        Mockito.verify(mockExecChain, Mockito.atMost(2)).proceed(Mockito.any(), Mockito.any());
+    }
 
     @Test
     public void testStaleWhileRevalidateYieldsToMustRevalidate()
@@ -495,20 +534,19 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         resp1.setHeader("ETag","\"etag\"");
         resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
         resp2.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15, must-revalidate");
         resp2.setHeader("ETag","\"etag\"");
         resp2.setHeader("Date", DateUtils.formatDate(now));
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         assertEquals(HttpStatus.SC_OK, result.getCode());
         boolean warning110Found = false;
@@ -544,20 +582,19 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         resp1.setHeader("ETag","\"etag\"");
         resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
         resp2.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15, proxy-revalidate");
         resp2.setHeader("ETag","\"etag\"");
         resp2.setHeader("Date", DateUtils.formatDate(now));
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         assertEquals(HttpStatus.SC_OK, result.getCode());
         boolean warning110Found = false;
@@ -593,8 +630,6 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         resp1.setHeader("ETag","\"etag\"");
         resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
 
-        backendExpectsAnyRequestAndReturn(resp1);
-
         final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
         req2.setHeader("Cache-Control","min-fresh=2");
         final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
@@ -602,12 +637,13 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         resp2.setHeader("ETag","\"etag\"");
         resp2.setHeader("Date", DateUtils.formatDate(now));
 
-        backendExpectsAnyRequestAndReturn(resp2);
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
 
-        replayMocks();
         execute(req1);
+
+        Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
+
         final ClassicHttpResponse result = execute(req2);
-        verifyMocks();
 
         assertEquals(HttpStatus.SC_OK, result.getCode());
         boolean warning110Found = false;
diff --git a/pom.xml b/pom.xml
index 9f7a8d2..f49b93e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -70,7 +70,6 @@
     <memcached.version>2.12.3</memcached.version>
     <slf4j.version>1.7.25</slf4j.version>
     <junit.version>4.13</junit.version>
-    <easymock.version>3.6</easymock.version>
     <mockito.version>3.10.0</mockito.version>
     <jna.version>5.2.0</jna.version>
     <hc.stylecheck.version>1</hc.stylecheck.version>
@@ -189,12 +188,6 @@
         <version>${mockito.version}</version>
         <scope>test</scope>
       </dependency>
-      <dependency>
-        <groupId>org.easymock</groupId>
-        <artifactId>easymock</artifactId>
-        <version>${easymock.version}</version>
-        <scope>test</scope>
-      </dependency>
     </dependencies>
   </dependencyManagement>