Giter Club home page Giter Club logo

restmock's Introduction

RESTMock

Android Arsenal Circle CI

REST API mocking made easy.

RESTMock is a library working on top of Square's okhttp/MockWebServer. It allows you to specify Hamcrest matchers to match HTTP requests and specify what response to return. It is as easy as:

RESTMockServer.whenGET(pathContains("users/andrzejchm"))
            .thenReturnFile(200, "users/andrzejchm.json");

Article

Table of contents

Setup

Here are the basic rules to set up RESTMock for Android

Step 1: Repository

Add it in your root build.gradle at the end of repositories:

allprojects {
	repositories {
		...
		maven { url "https://jitpack.io" }
	}
}

Step 2: Dependencies

Add the dependency

dependencies {
	androidTestImplementation 'com.github.andrzejchm.RESTMock:android:${LATEST_VERSION}' // see "Releases" tab for latest version
}

Step 3: Start the server

It's good to start server before the tested application starts, there are few methods:

a) RESTMockTestRunner

To make it simple you can just use the predefined RESTMockTestRunner in your UI tests. It extends AndroidJUnitRunner:

defaultConfig {
		...
    	testInstrumentationRunner 'io.appflate.restmock.android.RESTMockTestRunner'
    }
b) RESTMockServerStarter

If you have your custom test runner and you can't extend RESTMockTestRunner, you can always just call the RESTMockServerStarter. Actually RESTMockTestRunner is doing exactly the same thing:

public class MyAppTestRunner extends AndroidJUnitRunner {
	...
	@Override
	public void onCreate(Bundle arguments) {
		super.onCreate(arguments);
		RESTMockServerStarter.startSync(new AndroidAssetsFileParser(getContext()),new AndroidLogger());
		...
	}
	...
}

Step 4: Specify Mocks

a) Files

By default, the RESTMockTestRunner uses AndroidAssetsFileParser as a mocks file parser, which reads the files from the assets folder. To make them visible for the RESTMock you have to put them in the correct folder in your project, for example:

.../src/androidTest/assets/users/defunkt.json

This can be accessed like this:

RESTMockServer.whenGET(pathContains("users/defunkt"))
            .thenReturnFile(200, "users/defunkt.json");
b) Strings

If the response You wish to return is simple, you can just specify a string:

RESTMockServer.whenGET(pathContains("users/defunkt"))
            .thenReturnString(200, "{}");
c) MockResponse

If you wish to have a greater control over the response, you can pass the MockResponse

RESTMockServer.whenGET(pathContains("users/defunkt")).thenReturn(new MockResponse().setBody("").setResponseCode(401).addHeader("Header","Value"));
c) MockAnswer

You can always build dynamic MockResponses by using the RecordedRequest object

RESTMockServer.whenGET(pathContains("users/defunkt")).thenAnswer(new MockAnswer() {

            @Override
            public MockResponse answer(RecordedRequest request) {
                    return new MockResponse()
                            .setBody(request.getHeaders().get("header1"))
                            .setResponseCode(200);
            }
        });

Step 5: Request Matchers

You can either use some of the predefined matchers from RequestMatchers util class, or create your own. remember to extend from RequestMatcher

Step 6: Specify API Endpoint

The most important step, in order for your app to communicate with the testServer, you have to specify it as an endpoint for all your API calls. For that, you can use the RESTMockServer.getUrl(). If you use Retrofit, it is as easy as:

RestAdapter adapter = new RestAdapter.Builder()
                .baseUrl(RESTMockServer.getUrl())
                ...
                .build();

take a look at #68 for better reference

HTTPS

By default, RESTMockServer will serve responses using Http. In order to use HTTPS, during initialization you have to pass RESTMockOptions object with useHttps set to true:

RESTMockServerStarter.startSync(new AndroidAssetsFileParser(getContext()),new AndroidLogger(), new RESTMockOptions.Builder().useHttps(true).build());

there is a possibility to set up your own SSLSocketFactory and TrustManager, if you do not specify those, then default ones will be created for you. You can easly retrieve them with RESTMockServer.getSSLSocketFactory() and RESTMockServer.getTrustManager() to be able to build your client that will accept the default certificate:

new OkHttpClient.Builder().sslSocketFactory(RESTMockServer.getSSLSocketFactory(), RESTMockServer.getTrustManager()).build();

A sample how to use https with RESTMock in android tests can be found in androidsample gradle module within this repository.

Response chains

You can chain different responses for a single request matcher, all the thenReturn*() methods accept varags parameter with response, or you can call those methods multiple times on a single matcher, examples:

RESTMockServer.whenGET(pathEndsWith(path))
                .thenReturnString("a single call")
                .thenReturnEmpty(200)
                .thenReturnFile("jsonFile.json");

or

RESTMockServer.whenGET(pathEndsWith(path))
                .thenReturnString("a single call", "answer no 2", "answer no 3");

Response body delays

Delaying responses is accomplished with the delayBody(TimeUnit timeUnit, long delay) and delayHeaders(TimeUnit timeUnit, long delay) method. Delays can be specified in chain, just like chaining responses:

RESTMockServer.whenGET(pathEndsWith(path))
                .thenReturnString("a single call")
                .delayBody(TimeUnit.SECONDS, 5)
                .delayBody(TimeUnit.SECONDS, 10)
                .delayBody(TimeUnit.SECONDS, 15);

or

RESTMockServer.whenGET(pathEndsWith(path))
                .thenReturnString("a single call")
                .delayBody(TimeUnit.SECONDS, 5, 10, 15);

Which will result in 1st response body being delayed by 5 seconds, 2nd response by 10 seconds and 3rd, 4th, 5th... by 15 seconds.

Response header delays

Mechanics of the responseHeader(...) method are the same as those in responseBody(...). The only difference is that response headers are being delivered with a delay. This comes handy if your app is acting on response headers, which would've been delivered immediately if you used the delayBody(...) method.

Interleaving delays with responses

Check out this example:

RESTMockServer.whenGET(pathEndsWith(path))
                .thenReturnString("1st call")
                .delay(TimeUnit.SECONDS, 5)
                .thenReturnString("2nd call")
                .delay(TimeUnit.SECONDS, 10)
                .delay(TimeUnit.SECONDS, 15)
                .thenReturnString("3rd call")
                .delay(TimeUnit.SECONDS, 20, 30, 40)

this will result in 1st call being delayed by 5 seconds, 2nd call delayed by 10 seconds, 3rd call delayed by 15 seconds, another one by 20 seconds, and another by 30 seconds, and then every consecutive response with 40 seconds delay

Request verification

It is possible to verify which requests were called and how many times thanks to RequestsVerifier. All you have to do is call one of these:

//cheks if the GET request was invoked exactly 2 times
RequestsVerifier.verifyGET(pathEndsWith("users")).exactly(2);

//cheks if the GET request was invoked at least 3 times
RequestsVerifier.verifyGET(pathEndsWith("users")).atLeast(3);

//cheks if the GET request was invoked exactly 1 time
RequestsVerifier.verifyGET(pathEndsWith("users")).invoked();

//cheks if the GET request was never invoked
RequestsVerifier.verifyGET(pathEndsWith("users")).never();

Additionaly, you can manualy inspect requests received by RESTMockServer. All you have to do is to obtain them trough:

//gets 5 most recent requests received. (ordered from oldest to newest)
RequestsVerifier.takeLast(5);

//gets 5 oldest requests received. (ordered from oldest to newest)
RequestsVerifier.takeFirst(5);

//gets all GET requests.  (ordered from oldest to newest)
RequestsVerifier.takeAllMatching(isGET());

Logging

RESTMock supports logging events. You just have to provide the RESTMock with the implementation of RESTMockLogger. For Android there is an AndroidLogger implemented already. All you have to do is use the RESTMockTestRunner or call

RESTMockServerStarter.startSync(new AndroidAssetsFileParser(getContext()),new AndroidLogger(), new RESTMockOptions());

or

RESTMockServer.enableLogging(RESTMockLogger)
RESTMockServer.disableLogging()

Android Sample Project

You can check out the sample Android app with tests here

Donation

If you think the library is awesome and want to buy me a beer, you can do so by sending some...

  • Ethereum ETH here: 0xf7354a0F7B34A380f6d68a2661bE465C10D6AEd7
  • Bitcoin BTC here: 12bU3BMibFqbBBymaftXTDnoHojFymD7a6
  • NEO NEO or GAS here: AX1ovzRN2N28WJrtehjYXjwtHSvcqva6Ri

License

Copyright (C) 2016 Appflate.io

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

restmock's People

Contributors

alapshin avatar andrzejchm avatar bryant1410 avatar jlmcdonnell avatar justintuchek avatar jwir3 avatar mirland avatar oradkovsky avatar piasy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

restmock's Issues

SSLHandshakeException when using RESTMock

Hi,

First of all thanks for the great library. I've used RESTMock in the past around 2yrs back and everything worked smoothly. I was trying it today and faced some issues.

I have a simple test method

@Test
fun shouldShowLoader() {
    RESTMockServer.whenGET(RequestMatchers.pathContains("abc.json"))
            .thenReturnFile(200, "abc/abc.json")

    activityRule.launchActivity(null)

    onView(withId(R.id.errorContainer)).check(matches(isDisplayed()))
}

I'm getting this exception in logs :

URL its hitting: https://localhost:40882/abc.json?key=abc&q=mock

I/MockWebServer: MockWebServer[40882] connection from /127.0.0.1 failed: javax.net.ssl.SSLHandshakeException: Handshake failed D/OkHttp: <-- HTTP FAILED: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

Tried a lot of solutions but nothing worked out. I'm using Android P for testing.

The app is working fine without any issue in production. Its just the UI tests I'm struggling with.

Best Practice - how to replace a mapped endpoint response?

I was wondering if you have the best practice for replacing an already mapped endpoint.

I have endpoints mapped as such (just an example):

val getSettings: MatchableCall
        get() = whenRequested(allOf(
                isGET(),
                pathStartsWith("/api/v2/"),
                pathEndsWithIgnoringQueryParams("settings.json")
        ))

I then have an initDefaultMocks function that maps endpoints with default responses. See below:

fun initDefaultMocks() {
        postOauthMobile.thenReturnEmpty(200)
        getSettings.thenReturnEmpty(200)
        getUser.thenReturnEmpty(200)
        }

What that means is that sometimes I need to remove the mock and add a different one (unique cases with their own mocks).
What is the best way to replace that endpoint mock? Ideally I'd like to be able to provide an endpoint matcher and remove/replace the response for that 🤔

Response ERROR

Why I get the below error.

E/RESTMock: <- Response ERROR: NOT MOCKED: GET /ServiceStatus HTTP/1.1

Below is my code

@rule
public ActivityTestRule mActivityRule = new ActivityTestRule<>(HomePageActivity.class,false,true);

@Before
public void init() {


    //be sure to reset it before each test!
    RESTMockServer.reset();


}



@Test
public void shouldDisplayUpgradeRequiredMessage() {



    RESTMockServer.whenGET(pathContains("ServiceStatus"))
            .thenReturnFile(200, "ServiceStatus.json");

    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }


    RequestsVerifier.verifyGET(pathEndsWith("ServiceStatus.json")).exactly(1);


    onView(ViewMatchers.withId(R.id.tvServiceMessage)).check(matches(withText(UpgradeMessage)));

}

This is the detailed stacktrace
04-25 19:55:28.008 17309-17309/CommCore.avv.Debug D/LifecycleMonitor: Lifecycle status change: CommCore.avv.app.homepage.HomePageActivity@c0dfeb5 in: STARTED
04-25 19:55:28.009 17309-17340/CommCore.avv.Debug D/OkHttp: --> GET http://localhost:44683/ServiceStatus http/1.1
04-25 19:55:28.009 17309-17340/CommCore.avv.Debug D/OkHttp: Referer: http://localhost:44683/?OS=Android&OSVersion=7.1.1&Make=unknown&Model=Android SDK built for x86&AppVersion=4.05.0&ScreenWidth=1080&ScreenHeight=1776&UUID=641c8afde8f574b1
04-25 19:55:28.009 17309-17340/CommCore.avv.Debug D/OkHttp: --> END GET
04-25 19:55:28.010 17309-17309/CommCore.avv.Debug D/LifecycleMonitor: Lifecycle status change: CommCore.avv.app.homepage.HomePageActivity@c0dfeb5 in: RESUMED
04-25 19:55:28.022 17309-17341/CommCore.avv.Debug D/RESTMock: -> New Request: GET /ServiceStatus HTTP/1.1
04-25 19:55:28.022 17309-17341/CommCore.avv.Debug E/RESTMock: <- Response ERROR: NOT MOCKED: GET /ServiceStatus HTTP/1.1
04-25 19:55:28.023 17309-17341/CommCore.avv.Debug I/MockWebServer: MockWebServer[44683] received request: GET /ServiceStatus HTTP/1.1 and responded: HTTP/1.1 500 Server Error
04-25 19:55:28.024 17309-17340/CommCore.avv.Debug D/OkHttp: <-- 500 Server Error http://localhost:44683/ServiceStatus (15ms)

Needs Explaination why we need to change base url in application

RestAdapter adapter = new RestAdapter.Builder()
.baseUrl(RESTMockServer.getUrl())
.build();

You mentioned that we need to change base url in native code by using TestApplicatiion. Why you need to this. The interceptor needs to works irespective to URL. sometime application has multiple URL and some developer dont want to change the code. It will be helpful that you provide the explaination. I read the tutorial mentioned in above step. I need to integrate this library for Espresso Tests

AndroidX compatability

Hi 👋

Would you be open to accepting a PR for enabling Android X support - or is it something that is already road mapped?

Upgrading to OkHttp 4.0.0

I've upgraded my app to OkHttp 4.0.0 and I am no longer able to run RestMock tests. The app crashes with this error:

java.lang.NoSuchMethodError: No static method initializeInstanceForTests()V in class Lokhttp3/internal/Internal; or its super classes (declaration of 'okhttp3.internal.Internal' appears in /data/app/???-Erfx_-ETp-Rrby47elr7mg==/base.apk!classes34.dex)
    at okhttp3.mockwebserver.MockWebServer.<clinit>(MockWebServer.java:105)
    at io.appflate.restmock.RESTMockServer.init(RESTMockServer.java:70)
    at io.appflate.restmock.RESTMockServerStarter$1.run(RESTMockServerStarter.java:56)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
    at java.lang.Thread.run(Thread.java:764)

Issue with Test not passing

Hi Below is the error i keep getting when using Rest Mock Server in my project.

I have followed the sample and still cant make it work.
i don't have this issue when i copy my json or test to the android Sample.

Below are some screenshots of my project state to see if i can be assisted.

Screenshot 2019-10-04 at 1 43 51 PM

Screenshot 2019-10-04 at 1 52 01 PM

Screenshot 2019-10-04 at 1 52 18 PM

I am using v0.4.0 of the Library to test this

converting android sample project to Kotlin gives ClassNotFoundException

When I convert MainActivityTest.java to kotlin, it gives below error:

$ adb shell am instrument -w -r -e notPackage org.bouncycastle.pqc.crypto.qtesla   -e debug false -e class 'io.appflate.restmock.androidsample.tests.MainActivityTest' io.appflate.restmock.androidsample.test/io.appflate.restmock.androidsample.CustomTestRunner
Connected to process 8637 on device 'lge-nexus_5x-024c6d7f5e96d337'.

Started running tests


java.lang.ClassNotFoundException: io.appflate.restmock.androidsample.tests.MainActivityTest
at java.lang.Class.classForName(Native Method)
at java.lang.Class.forName(Class.java:400)
at androidx.test.internal.runner.TestLoader.doCreateRunner(TestLoader.java:72)
at androidx.test.internal.runner.TestLoader.getRunnersFor(TestLoader.java:104)
at androidx.test.internal.runner.TestRequestBuilder.build(TestRequestBuilder.java:793)
at androidx.test.runner.AndroidJUnitRunner.buildRequest(AndroidJUnitRunner.java:547)
at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:390)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1932)
Caused by: java.lang.ClassNotFoundException: Didn't find class "io.appflate.restmock.androidsample.tests.MainActivityTest" on path: DexPathList[[zip file "/system/framework/android.test.runner.jar", zip file "/data/app/io.appflate.restmock.androidsample.test-1/base.apk", zip file "/data/app/io.appflate.restmock.androidsample-2/base.apk"],nativeLibraryDirectories=[/data/app/io.appflate.restmock.androidsample.test-1/lib/arm64, /data/app/io.appflate.restmock.androidsample-2/lib/arm64, /system/lib64, /vendor/lib64]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:380)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
... 8 more

Tests ran to completion.

Gradle Dependency issue

Hi,

I have added the dependency as

dependencies {
androidTestCompile 'com.github.andrzejchm.RESTMock:android:0.2.1'
}
also included the maven url in the project build.gradle.

But I am not able to access the api.

When I keep the below as dependency

dependencies {
compile 'com.github.andrzejchm.RESTMock:android:0.2.1'
}

then I am able to access the API.

Could you please let me know which one to follow

Thanks,
Chaitanya

Add delay to a response.

Hi! would be really nice if we were able to do something like this:

whenGET(pathEndsWith("pokemon/1/"))
    .thenReturnFile(200, "getPokemonDetails_200.json")
    .delay(3, TimeUnit.SECONDS); // this!

Are there any plans to include something similar?

Thanks! Great work!

IncompatibleClassChangeError

After upgrading from RESTMock 0.1.1 to RESTMock 0.1.3, I'm beginning to see the following exceptions in tests that use RESTMock:

java.lang.IncompatibleClassChangeError: Implementing class
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
    at org.robolectric.internal.bytecode.InstrumentingClassLoader.findClass(InstrumentingClassLoader.java:153)
    at org.robolectric.internal.bytecode.InstrumentingClassLoader.loadClass(InstrumentingClassLoader.java:95)
    at okhttp3.logging.HttpLoggingInterceptor$Logger$1.log(HttpLoggingInterceptor.java:109)
    at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:157)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:170)
    at okhttp3.RealCall.execute(RealCall.java:60)
    at retrofit2.OkHttpCall.execute(OkHttpCall.java:174)
    at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$RequestArbiter.request(RxJavaCallAdapterFactory.java:171)
    at rx.Subscriber.setProducer(Subscriber.java:209)
    at rx.Subscriber.setProducer(Subscriber.java:205)
    at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:152)
    at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:138)
    at rx.Observable.subscribe(Observable.java:8553)
    at rx.Observable.subscribe(Observable.java:8520)
    at com.thisclicks.core.api.AvatarServiceApiTest.itShouldReturnUserDoesNotHaveAValidAvatar(AvatarServiceApiTest.java:58)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:251)
    at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:188)
    at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:54)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:152)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:105)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:56)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:64)
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:50)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
    at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
    at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:106)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:360)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
    at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

I suspect this is caused by one or more of the libraries we're using being a different version than that of RESTMock's libraries. Specifically, I suspect one of the OkHttp libraries, but I'm not sure how to track down exactly which one. For our project, we use the following library definition:

    compile "com.android.support:appcompat-v7:23.2.1"
    compile 'com.google.code.gson:gson:2.3.0'
    compile 'io.reactivex:rxjava:1.1.0'

    compile 'io.reactivex:rxandroid:1.1.0'
    compile 'com.trello:rxlifecycle:0.4.0'
    compile 'com.trello:rxlifecycle-components:0.4.0'

    apt 'com.google.dagger:dagger-compiler:2.0.1'
    compile 'com.google.dagger:dagger:2.0.1'

    compile "com.squareup.retrofit2:retrofit:2.0.2"
    compile "com.squareup.retrofit2:adapter-rxjava:2.0.2"
    compile "com.squareup.retrofit2:converter-gson:2.0.2"
    compile 'com.squareup.okhttp3:logging-interceptor:3.2.0'
    compile "au.com.gridstone.rxstore:rxstore:5.0.2"
    compile "au.com.gridstone.rxstore:converter-gson:5.0.2"
    compile "com.android.support:design:23.2.1"
    compile "com.google.android.gms:play-services-base:8.4.0"

    compile "com.google.android.gms:play-services-plus:8.4.0"

    compile "com.google.android.gms:play-services-location:8.4.0"
    compile "com.google.android.gms:play-services-auth:8.4.0"
    compile('com.crashlytics.sdk.android:crashlytics:2.5.5@aar') {
        transitive = true;
    }
    compile "joda-time:joda-time:2.8.2"

    testCompile 'junit:junit:4.12'
    testCompile 'com.squareup.assertj:assertj-android:1.1.0'
    testCompile 'org.robolectric:robolectric:3.0'
    testCompile 'org.mockito:mockito-core:1.9.5'
    testCompile 'org.powermock:powermock-module-junit4:1.6.5'
    testCompile 'org.powermock:powermock-api-mockito:1.6.5'
    testCompile 'com.github.andrzejchm.RESTMock:android:0.1.3'
    deployerJars 'org.apache.maven.wagon:wagon-ssh:2.2'
    deployerJars 'org.apache.maven.wagon:wagon-ssh-external:2.2'

This is blocking me from testing two other patches to RESTMock that I had previously planned on posting.

I have gone through and looked at the dependency changes from 0.1.1 -> 0.1.3, and changed the dependencies that overlap to match the RESTMock versions, but without success. I'm wondering if there is something else I could do to try and debug this problem so we can upgrade our library to RESTMock v0.1.3.

Proxying feature

Add a proxying feature which allows unmatched requests to be forwarded to the actual server. This would allow us to use RESTMock for developing new REST services without the implementation of the REST endpoint on the actual server.

See WireMock for more information: http://wiremock.org/docs/proxying/

Missing error cause logging on failure

Hi,
It would be great if RESTMock could inform us about the reason of the failure.
At this moment, when something goes wrong, it provides pretty generic information about the error:

INFO: MockWebServer[50062] received request: GET /channel/list HTTP/1.1 and responded: HTTP/1.1 500 Server Error

It's pretty hard to figure out what's wrong in such case - sometimes it's missing file, sometimes it's an other problem. More info would be really helpful.
Best regards

Gradle error for version 0.1.2

Hi, when I try to include androidTestCompile 'com.github.andrzejchm.RESTMock:android:0.1.2', I get the following error:

Error:Module 'com.github.andrzejchm.RESTMock:android:0.1.2' depends on one or more Android Libraries but is a jar

But version 0.1.1 works fine with no issues.

Thoughts?

ConcurrentModificationException when resetting the server

Hi! We've been using restmock for a while now, and we're often getting this Exception in many of our builds while running Android UI tests.

We suspect it's happening when we reset the matched requests before a new test, and at the same time there are is request from the previous test still running, because Android shares the process between tests.

More specifically, this happens when we invoke RestMockServer.reset() after a test, and at the same time the method getMatchedRequests() is iterating the matchableCalls list because some background thread made a request after the test already ended.

We try to avoid this by using IdlingResources to make Espresso wait for our background tasks, but having almost 300 tests the probability of leaking some request is quite high.

I can think of an easy solution by replacing the matchableCalls type from LinkedList to CopyOnWriteArrayList.
This might cause the leaked request to not find a matched call and return an http 500 error , but that should be OK since the original test already finished, and I believe that's the currently expected behaviour. It's not a silver bullet solution to flaky tests, but it should help us quite a lot.

What do you think? Is it OK if I open a Pull Request? Or do you think there may be a better approach?

Here's the full stacktrace and some logs of the crash:

D/RESTMock(10912): -> New Request:	GET /api/1/dictionary/professional-level HTTP/1.1
D/RESTMock(10912): ## Removing all responses
I/MockWebServer(10912): MockWebServer[39702] done accepting connections: Socket closed
I/MicrophoneInputStream( 2161): mic_close gzi@1f273041
I/HotwordRecognitionRnr( 2161): Hotword detection finished
I/HotwordRecognitionRnr( 2161): Stopping hotword detection.
I/System.out(10912): path: /api/1/dictionary/professional-level
--------- beginning of crash
E/AndroidRuntime(10912): FATAL EXCEPTION: OkHttp Http2Connection
E/AndroidRuntime(10912): Process: net.infojobs.mobile.android.debug, PID: 10912
E/AndroidRuntime(10912): java.util.ConcurrentModificationException
E/AndroidRuntime(10912): 	at java.util.LinkedList$LinkIterator.next(LinkedList.java:124)
E/AndroidRuntime(10912): 	at io.appflate.restmock.MatchableCallsRequestDispatcher.getMatchedRequests(MatchableCallsRequestDispatcher.java:96)
E/AndroidRuntime(10912): 	at io.appflate.restmock.MatchableCallsRequestDispatcher.dispatch(MatchableCallsRequestDispatcher.java:41)
E/AndroidRuntime(10912): 	at okhttp3.mockwebserver.MockWebServer$Http2SocketHandler.onStream(MockWebServer.java:942)
E/AndroidRuntime(10912): 	at okhttp3.internal.http2.Http2Connection$ReaderRunnable$1.execute(Http2Connection.java:673)
E/AndroidRuntime(10912): 	at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
E/AndroidRuntime(10912): 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
E/AndroidRuntime(10912): 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
E/AndroidRuntime(10912): 	at java.lang.Thread.run(Thread.java:818)
W/ActivityManager( 1489): Error in app net.infojobs.mobile.android.debug running instrumentation ComponentInfo{net.infojobs.mobile.android.debug.test/com.infojobs.app.testutil.InfojobsTestRunner}:
W/ActivityManager( 1489):   java.util.ConcurrentModificationException
W/ActivityManager( 1489):   java.util.ConcurrentModificationException
D/AndroidRuntime(10900): Shutting down VM

got Problem with relative paths from android gradle plugin 3.5.0

Hello,
I've got an issue using the gradle Plugin. With the last update I'm not able to open relative paths in my project. The issue appeared by upgrading com.android.tools.build:gradle from 3.4.2 to 3.5.0.
thenReturnFile is not able to open asset files and crashes when using mockserver in connected tests with the following error:

E/RESTMock: <- Response FILE READ ERROR
   java.io.FileNotFoundException

We could have brought it to work by using the following code:

context.getAssets()
    .open(**FILEPATH**)
    .use { Log.d("test", it.bufferedReader().readLine()) }

Multiple Responses Same Path

Hiya,

Awesome library!
However.

We are in a situation where we want to test if a pull to refresh succeeds successfully.
See pseudo code

fun onPullToRefreshTheDataShouldBeDifferent() {
        whenGET(pathEndsWith("/some/path")
                .thenReturnFile("/file_with_json_1.json")
                .thenReturnFile("/file_with_json_2.json"

        launchActivity()
        doAssertionViewHolder()

        swipeToRefresh()

       doAssertionDifferentViewHolderShouldNowBeVisible()
}

The RESTMockWebServer seems to always return the first defined JSON ("/file_with_json_1.json").
Does this work like intended?
Because I don't see any samples like this on your repo

Thanks In advance.

Tim

MockWebServer initialization problem

I have a test setup something like:

@RunWith(AndroidJUnit4.class)
public class SomeTest {

    @Before
    public void setUp() {
        AndroidAssetsFileParser restMockFileParser 
                = new AndroidAssetsFileParser(InstrumentationRegistry.getContext());
        RESTMockServerStarter.startSync(restMockFileParser);

 ...

I'm getting the following error when running any test

java.lang.NoSuchMethodError: No static method initializeInstanceForTests()V in class Lokhttp3/internal/Internal; or its super classes (declaration of 'okhttp3.internal.Internal' appears in /data/app/com.example.debug-1/base.apk)

I already have my proguard keeping everything in my test apk:

-dontnote **
-dontobfuscate

# hacky way to tell proguard to keep EVERYTHING
-keep class !non.existant.package.** { *; }
-dontwarn !non.existant.package.**,**

I can see the initializeInstanceForTests method in the test apk:

test_apk

However the method has been stripped from my app apk. This is expected since it isn't used in the app.

app_apk

I can solve my problem by telling proguard to keep Internal in my app apk:

-keepclassmembers public class okhttp3.internal.Internal{
    *;
}

But why does this work?

I can follow the stack trace but everything looks like it shoiuld be happening in the test apk.

RESTMockServerStarter.startSync(restMockFileParser);

// produces the callstack:

// RESTMockServer.init(mocksFileParser, logger);
// ...
// RESTMockServer.mockWebServer = new MockWebServer();
// ...
// Internal.initializeInstanceForTests();

Why/how is RestMock setting up MockWebServer in my app apk not in my test apk?

Crash with proguard

Hi,
the REST-Mock crashes when proguard obfuscation is applied and HTTPS is enabled.
Here is the stacktrace:
2020-05-08 14:19:07.799 13382-13475/xxx E/AndroidRuntime: FATAL EXCEPTION: pool-10-thread-1
Process: xxx, PID: 13382
java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.String java.security.PublicKey.getAlgorithm()' on a null object reference
at java.security.KeyStore$PrivateKeyEntry.(KeyStore.java:576)
at java.security.KeyStore$PrivateKeyEntry.(KeyStore.java:526)
at java.security.KeyStoreSpi.engineGetEntry(KeyStoreSpi.java:485)
at java.security.KeyStore.getEntry(KeyStore.java:1560)
at com.android.org.conscrypt.KeyManagerImpl.(KeyManagerImpl.java:72)
at com.android.org.conscrypt.KeyManagerFactoryImpl.engineGetKeyManagers(KeyManagerFactoryImpl.java:115)
at javax.net.ssl.KeyManagerFactory.getKeyManagers(KeyManagerFactory.java:305)

According to this article the reason may be, that a deprecated version of spongycastle code to generate a self signed certificate is used:
http://quabr.com:8182/59848764/how-to-fix-proguard-removes-java-security-code

Regards
Benjamin

Test delayed data streaming

I have a PUT request that slowly streams bytes to a server. Internal to this call I track total bytes uploaded. I now want to write a test asserting that X bytes were uploaded. To do this I mostly need to keep RESTMock from immediately returning 200. It should let me write some bytes and only later return a response code. IIUC from other issues, .delay() operator only delays the response body, not the response code. Is there any way to change this?

VerifyError: Cannot inherit from final class

Kotlin version: 1.1.0
RESTMock Version: 0.2.1

Hi

I'm trying to use your library in Kotlin with JUnit robolectric tests and getting following exception:

Exception in thread "pool-3-thread-1" java.lang.VerifyError: Cannot inherit from final class
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
	at org.robolectric.internal.bytecode.SandboxClassLoader.maybeInstrumentClass(SandboxClassLoader.java:138)
	at org.robolectric.internal.bytecode.SandboxClassLoader.findClass(SandboxClassLoader.java:101)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at io.appflate.restmock.RESTMockServer.init(RESTMockServer.java:54)
	at io.appflate.restmock.RESTMockServerStarter$1.run(RESTMockServerStarter.java:44)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)

This exception seems to be thrown within RESTMockServer.init() method, when new MockWebServer object is trying to be created.
It's also kinda weird because everything works well in instrumented android tests.
I created a sample kotlin project with your library here where this problem exists, so I would be very grateful if you could take a look on this in your spare time.
As you will see, I included there all stuff from your robolectric sample.

Have you ever met similar problem while working on your library?

Expose underlying MockWebServer or support assertions

MockWebServer allows us to assert that certain web requests were made:

 // Optional: confirm that your app made the HTTP requests you were expecting.
  RecordedRequest request1 = server.takeRequest();
  assertEquals("/v1/chat/messages/", request1.getPath());
  assertNotNull(request1.getHeader("Authorization"));

  RecordedRequest request2 = server.takeRequest();
  assertEquals("/v1/chat/messages/2", request2.getPath());

  RecordedRequest request3 = server.takeRequest();
  assertEquals("/v1/chat/messages/3", request3.getPath());

This functionality isn't wrapped by RESTMock. It would be great if we could used this half of MockWebServer.

At the very least, RESTMock should expose the mock server so we can manually assert; TL;DR RESTMockServer.mockWebServer should be public or exposed by getter

Version 0.3.2 is not working doing as the sample app

The sample app is using RESTMockServer.getSSLSocketFactory() and RESTMockServer.getTrustManager() in Retrofit OkHttp and is not working with it, returning the following error:

12-18 18:59:12.629 2471-3623/? E/NetdConnector: RCV <- {600 Iface linkstate wlan0 up} 12-18 18:59:32.638 2471-3623/? E/NetdConnector: RCV <- {600 Iface linkstate wlan0 up} 12-18 18:59:38.517 18827-18896/br.com.ioasys.fdc E/AndroidRuntime: FATAL EXCEPTION: OkHttp Http2Connection Process: br.com.ioasys.fdc, PID: 18827 java.lang.NoSuchMethodError: No virtual method getRequestHeaders()Ljava/util/List; in class Lokhttp3/internal/http2/Http2Stream; or its super classes (declaration of 'okhttp3.internal.http2.Http2Stream' appears in /data/app/br.com.ioasys.fdc-2/base.apk:classes3.dex) at okhttp3.mockwebserver.MockWebServer$Http2SocketHandler.readRequest(MockWebServer.java:937) at okhttp3.mockwebserver.MockWebServer$Http2SocketHandler.onStream(MockWebServer.java:910) at okhttp3.internal.http2.Http2Connection$ReaderRunnable$1.execute(Http2Connection.java:674) at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) at java.lang.Thread.run(Thread.java:818) 12-18 18:59:38.625 2471-3672/? E/ActivityManager: Will notifyMovedToHome from moveHomeStack 12-18 18:59:38.625 2471-3672/? E/ActivityManager: Broadcasting Moved to Home screen Intent 12-18 18:59:52.654 2471-3623/? E/NetdConnector: RCV <- {600 Iface linkstate wlan0 up} 12-18 19:00:12.375 2471-3627/? E/native: do suspend false

If I remove these methods related to SSL, it throws me an error related to SSL.

Retrofit version: 2.5.0
OkHttp version: 3.12.0

Delay not working

Hello, I've got a little issue with the delay method :

The code

Here is the code, in a jUnit test:

        RESTMockServer.whenRequested(pathContains(BernardAPI.LOGOUT))
                .thenReturnString(200, "test response")
                .delay(TimeUnit.SECONDS, 30);
        
        RESTMockServer.enableLogging(new RESTMockLogger() {
            @Override
            public void log(String message) {
                String m = message;
            }

            @Override
            public void error(String errorMessage) {
                String m = errorMessage;
            }

            @Override
            public void error(String errorMessage, Throwable exception) {
                String m = errorMessage;
            }
        });

        Boolean result = bernardWebService.logout();

I run it on debug mode and I have breakpoints in each of the RESTMockLogger methods.

Expected behavior

When I run bernardWebService.logout(), it triggers a HTTP Request on a logout URL, catched by RESTMockServer. As I defined a delay of 30 seconds, I expect the Mock webserver to wait 30 seconds before sending a response (and I hope to get a timeout to test this case).

Actual behavior

When bernardWebService.logout() is triggered, I get into RESTMockLogger.log with this message:

-> New Request:	GET /api/logout HTTP/1.1

About a second later, I'm back in my RESTMockLogger.log method with this message:

<- Response:	HTTP/1.1 200 OK

As you can see, no delay is applied.

I tried with days and milliseconds unit. I tried to call the delay method before and after calling thenReturnString, it still doesn't work.

Unable to get RESTMock to work with external libraries of RESTapi

Hi, first of all I really admire the simplicity.

but somehow I find it difficult to implement in my project.
so my project consist of multiple libraries in which consist of multiple api call from each Library.

I've followed all the guide given but still unable to mock the rest data (the RESTMock doesn't intercept the API call)

defaultConfig {
testInstrumentationRunner 'io.appflate.restmock.android.RESTMockTestRunner'
}
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
    androidTestCompile 'com.android.support.test:runner:0.5'
    androidTestCompile 'com.android.support.test:rules:0.5'
    androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
    androidTestCompile ('com.github.andrzejchm.RESTMock:android:0.2.2') {
        exclude module: 'okhttp'
    } 

exclude because of some conflict of versioning.

In my test:

public class SomeTest {

@Rule
    public ActivityTestRule<LauncherActivity> mActivityTestRule = new
            ActivityTestRule<>(LauncherActivity.class);

@Before
    public void startUp() {
        RESTMockServer.reset();
        retrofit = new Retrofit.Builder()
                .baseUrl(RESTMockServer.getUrl())
                .build();
    }

@Test
    public void someTest() {
        RESTMockServer.whenPOST(RequestMatchers.pathContains("customer/password_reset"))
                .thenReturnEmpty(200);
// do something here and there
}

@After
    public void tearDown() throws Exception {
        HelperTool.clearApplicationData(context);
    }

}

Above are some part of the code, I'm using espresso as you can notice in my gradle. when I run the code, the logcat still showing me that it points to my backend server. instead of RESTMockServer.getUrl()

Is there anything I missed here? Sorry still new in this

"There are more than one response matching" for identical request/response combination?

Hi Andrew,

Thanks for great library!

I am facing either a bug or misuse situation, here is the scenario:

  1. Let's say we have instrumentation test that does mocking like this:
    RESTMockServer.whenGET(pathContains(".../" + valueLongId)) .thenReturnString(responseJson)
  2. The above runs just fine as a separate test
  3. Let's copy-paste the test, keeping the above mocking identical. Once our suite of 2 tests runs, one of the tests will fail with "java.lang.IllegalStateException: There are more than one response matching this request..."

For the above scenario, I would expect mocking to work because from test perspective both specified requests and responses are identical? Lib version 0.2.1.

Create new release

There have been a few features that have gone into the current development trunk. At my work, we're using jwir3/RESTMock instead of RESTMock's official distribution, because the features haven't been included yet.

This is just a friendly reminder to prepare a distribution. @andrzejchm if you don't have time and would like some assistance, I'd be happy to help maintain the package for you if you give me access.

RequestMatcher not working

Hi, I'm using RESTMock and I'm loving it. But I was trying to mock GET request with path matchers but I always got MOCK ERRORS. When I'm doing a simple request to http://test-server.org/get/true and I add a matcher pathEndsWith("true") I get a NOT MOCKED ERROR. The exact same request but without the pathEndsWith("true") works. See example below with logs.

whenGET(pathEndsWith("true")).thenReturnFile(RESTMOCK_GET_PATH_URL)
D/RESTMock: ## Adding new response for:	(HTTP method is: GET and url ends with: true)
D/OkHttp: --> GET http://test-server.org/get/true http/1.1
D/OkHttp: --> END GET
D/RESTMock: -> New Request:	GET / HTTP/1.1
E/RESTMock: <- Response ERROR:	NOT MOCKED: GET / HTTP/1.1
D/OkHttp: <-- 500 Server Error http://localhost:36316/ (4ms)
D/OkHttp: Content-Length: 10
D/OkHttp: NOT MOCKED
D/OkHttp: <-- END HTTP (10-byte body)
I/MockWebServer: MockWebServer[36316] received request: GET / HTTP/1.1 and responded: HTTP/1.1 500 Server Error
whenRequested(isGET()).thenReturnFile(RESTMOCK_GET_PATH_URL)
D/RESTMock: ## Adding new response for:	HTTP method is: GET
D/OkHttp: --> GET http://test-server.org/get/true http/1.1
D/OkHttp: --> END GET
D/RESTMock: -> New Request:	GET / HTTP/1.1
D/RESTMock: <- Response:	HTTP/1.1 200 OK
I/MockWebServer: MockWebServer[40123] received request: GET / HTTP/1.1 and responded: HTTP/1.1 200 OK
D/OkHttp: <-- 200 OK http://localhost:40123/ (5ms)
D/OkHttp: Content-Length: 27
D/OkHttp: {  "getIsSuccessful": true}
D/OkHttp: <-- END HTTP (27-byte body)

Mocked Server is Not Using HTTPS

The Mocked Server that is set up by RESTMock is using HTTP. Android SDK 28 will start to deny any connection between servers that are not using HTTPs. So, all the applications tests that are using SDK 28 will fail. Because they can not find the Mocked Response, the instrumented tests are going to crash.

Method thenReturnFile() catch FileNotFoundException when file path contains blank space

When I write code like "RESTMockServer.whenGET(RequestMatchers.pathContains("users")).thenReturnFile(200, "json/users.json)",
the json file path is "E:\Android Projects\Leetcode\app\build\intermediates\sourceFolderJavaResources\test\debug\json\users.json", the test result is "java.io.FileNotFoundException: E:\Android%20Projects\Leetcode\app\build\intermediates\sourceFolderJavaResources\test\debug\json\users.json ". Is that any bug with the source code?

Allow multiple matchings for requests

Hi!

We've been using WireMock for a while now on our app, and we're happy with it. But it's sometimes too heavy for Android development, and this library seems like an awesome lightweight replacement for it. Congratulations!

There's just one feature missing that stops us from adopting it, and it's having multiple matchings for a given request. For example having a very specific matching and a default fallback:

/api/login -> login_error_response.json
/api/login?user=me&pass=123 -> login_success_response.json

In this case the first matching would also match the second request, and RESTMock will return a MORE_THAN_ONE_RESPONSE_ERROR.

In WireMock the user can specify a priority value, and it will pick the highest one (the lower number means more priority).
In my example, I would set a priority 2 for the default error, and priority 1 for the success case.

Another approach would be to just pick the last one added. Both solutions are not mutually exclusive, although I think the one with priorities is more robust.

What do you think about having this feature? I think it could be implemented as a withPriority(int) method on MatchableCall.

java.lang.NullPointerException: Attempt to invoke virtual method 'void io.appflate.restmock.MatchableCallsRequestDispatcher.removeAllMatchableCalls()

java.lang.NullPointerException: Attempt to invoke virtual method 'void io.appflate.restmock.MatchableCallsRequestDispatcher.removeAllMatchableCalls()' on a null object reference
    at io.appflate.restmock.RESTMockServer.reset(RESTMockServer.java:130)
    at com.example.e2etests.tests.AbstractTest.tearDown(AbstractTest.java:61)

We just started getting this crash on some our tests. It's happening in our test @After tear down method. Looking at the RESTMock source I guess this is possible if RestMock is not initialized. But we are initializing RESTMock when our test application starts

      AndroidAssetsFileParser restMockFileParser = new AndroidAssetsFileParser(InstrumentationRegistry.getContext());
        RESTMockServerStarter.startSync(restMockFileParser, new AndroidLogger());

And of course this was working just fine until this week. We are on version 0.3.2

Android P support

Attempting to use this library with SDK 28 results in:

java.lang.AssertionError: java.security.NoSuchAlgorithmException: The BC provider no longer provides an implementation for KeyPairGenerator.RSA.  Please see https://android-developers.googleblog.com/2018/03/cryptography-changes-in-android-p.html for more details.
        at okhttp3.tls.HeldCertificate$Builder.generateKeyPair(HeldCertificate.java:429)
        at okhttp3.tls.HeldCertificate$Builder.build(HeldCertificate.java:353)
        at io.appflate.restmock.SslUtils.localhost(SslUtils.java:44)
        at io.appflate.restmock.RESTMockServer.setUpHttps(RESTMockServer.java:91)
        at io.appflate.restmock.RESTMockServer.init(RESTMockServer.java:74)

I'm assuming that switching over to a different keyAlgorithm will work. Thoughts?

MockAnswer is not public

Hi,

First of all thank you for this awesome library!

We are trying to intercept the request to check POST's body parameters to return a response depending on a body parameter value. We are trying to do that with the method thenAnswer that needs as a parameter a io.appflate.restmock.MockAnswer but this interface is not public so we cannot access to it.

This is intentional? If it is, how can we achieve our goal to return a response depending on a body parameter?
If it's not, please could you make it public?

Thanks!

Upgrading to OkHttp 4.5.0

I've upgraded my app to OkHttp 4.5.0 and I am no longer able to run RestMock tests. The app crashes with this error:

java.lang.NoSuchMethodError: No static method start$default(Lokhttp3/internal/http2/Http2Connection;ZILjava/lang/Object;)V in class Lokhttp3/internal/http2/Http2Connection; or its super classes (declaration of 'okhttp3.internal.http2.Http2Connection at okhttp3.internal.concurrent.TaskRunner.runTask(TaskRunner.kt:116) at okhttp3.internal.concurrent.TaskRunner.access$runTask(TaskRunner.kt:42) at okhttp3.internal.concurrent.TaskRunner$runnable$1.run(TaskRunner.kt:65) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) at java.lang.Thread.run(Thread.java:764)

Version 0.3.2 with HTTPS throws exception on API Level 28

We have set up RESTMockServer using https by calling the following function in the Runner for our Android instrumented tests:

    private fun provisionMockServer() {
        RESTMockServer.enableLogging(LaundrappRESTMockLogger())
        val builder = RESTMockOptions.Builder().useHttps(true).build()
        RESTMockServerStarter.startSync(AndroidAssetsFileParser(context), AndroidLogger(), builder)
    }

We also then pass the SSLSocketFactory into our production app as shown in the example app. Using this method works well with Android versions up to Android Oreo (API 27) but running tests fails with the following exception on an emulator running API 28:

E/AndroidRuntime: FATAL EXCEPTION: pool-4-thread-1
    Process: com.company.ourapp, PID: 6114
    java.lang.AssertionError: java.security.NoSuchAlgorithmException: The BC provider no longer provides an implementation for KeyPairGenerator.RSA.  Please see https://android-developers.googleblog.com/2018/03/cryptography-changes-in-android-p.html for more details.
        at okhttp3.tls.HeldCertificate$Builder.generateKeyPair(HeldCertificate.java:429)
        at okhttp3.tls.HeldCertificate$Builder.build(HeldCertificate.java:353)
        at io.appflate.restmock.SslUtils.localhost(SslUtils.java:44)
        at io.appflate.restmock.RESTMockServer.setUpHttps(RESTMockServer.java:91)
        at io.appflate.restmock.RESTMockServer.init(RESTMockServer.java:74)
        at io.appflate.restmock.RESTMockServerStarter$1.run(RESTMockServerStarter.java:56)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:764)
     Caused by: java.security.NoSuchAlgorithmException: The BC provider no longer provides an implementation for KeyPairGenerator.RSA.  Please see https://android-developers.googleblog.com/2018/03/cryptography-changes-in-android-p.html for more details.
        at sun.security.jca.Providers.checkBouncyCastleDeprecation(Providers.java:563)
        at sun.security.jca.Providers.checkBouncyCastleDeprecation(Providers.java:330)
        at java.security.KeyPairGenerator.getInstance(KeyPairGenerator.java:303)
        at okhttp3.tls.HeldCertificate$Builder.generateKeyPair(HeldCertificate.java:425)
        at okhttp3.tls.HeldCertificate$Builder.build(HeldCertificate.java:353) 
        at io.appflate.restmock.SslUtils.localhost(SslUtils.java:44) 
        at io.appflate.restmock.RESTMockServer.setUpHttps(RESTMockServer.java:91) 
        at io.appflate.restmock.RESTMockServer.init(RESTMockServer.java:74) 
        at io.appflate.restmock.RESTMockServerStarter$1.run(RESTMockServerStarter.java:56) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
        at java.lang.Thread.run(Thread.java:764) 

Running the production app works fine so this has to be an issue with our testing setup - most likely RESTMockServer's usage of OkHttp looking at the stacktrace.

How to get Recorder request?

I'm looking for a method like server.takeRequest() which allows me to verify the body of the request, is there anyway?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.