Git Product home page Git Product logo

mockwebserver-path-dispatcher's Introduction

Javadocs Maven Central

MockWebServer path dispatcher

A helper for dispatching MockWebServer responses. It allows to easily mock responses with data stored in YAML files in resources/fixtures/ directory

Motivation

  • YAML used to store responses for more readability (compared to bare MockWebServer)
  • Android compatible (unlike MockWebServer+)
  • Concise dispatching logic implementation

Example

Code with MockWebServer path dispatcher:

fun pathCondition() {
    val dispatcher = FixtureDispatcher()
    // match all URLs with path starting with /prefix/ e.g. http://example.test/prefix/
    val factory = PathQueryConditionFactory("/prefix/")
    // match all URLs with path ending with "suffix" and return response from fixtures/body_path.yaml
    dispatcher.putResponse(factory.withPathSuffix("suffix"), "body_path")
    dispatcher.putResponse(factory.withPathSuffix("another_suffix"), "json_object")
    mockWebServer.setDispatcher(dispatcher)
}

Example YAML file at resources/fixtures/json_object.yaml:

statusCode : 200
headers:
- 'Content-Type: application/json'
body: >
    {
      "test": null
    }

Instead of defining body in yaml directly you can specify relative path to file with body:

statusCode : 404
headers:
- 'Content-Type: text/plain'
- "Vary: Accept-Encoding"
body: body.txt

You can force the request to fail by setting connectionFailure to true:

statusCode : 200
connectionFailure: true

Alternatively, you can specify not getting a response by simulating a timeout with timeoutFailure to true:

statusCode : 200
timeoutFailure: true

Code without MockWebServer path dispatcher:

fun bareMockWebServer() {
    val dispatcher = object : Dispatcher() {
        override fun dispatch(request: RecordedRequest): MockResponse {
            val path = request.requestUrl.encodedPath()
            if (path == "/prefix/suffix") {
                return MockResponse()
                        .setResponseCode(404)
                        .addHeader("Content-Type", "text/plain")
                        .addHeader("Vary", "Accept-Encoding")
                        .setBody("""{"test"}""")
            } else if (path == "/prefix/another_suffix") {
                return MockResponse()
                        .setResponseCode(200)
                        .addHeader("Content-Type", "application/json")
                        .setBody("{\n  \"test\": null\n}")
            }
            throw IllegalArgumentException("Unexpected request: $request")
        }
    }
    mockWebServer.setDispatcher(dispatcher)
}

See more examples at FunctionalTest.kt

API

FixtureDispatcher - when you want conditional fixture response mapping and enqueuing:

fun factory() {
    val dispatcher = FixtureDispatcher()
    val factory = PathQueryConditionFactory("/prefix/")
    // bar will be served for first matching requests
    dispatcher.enqueue(factory.withPathSuffix("suffix"), "bar")
    // bar will be served for second matching requests
    dispatcher.enqueue(factory.withPathSuffix("suffix"), "baz")
    // foo will be served by default (if there is nothing enqueued) for subsequent matching requests
    dispatcher.putResponse(factory.withPathSuffix("suffix"), "foo")    
    // qux will be served by default when there are no matching requests
    dispatcher.setFallbackResponse("qux")    
    mockWebServer.setDispatcher(dispatcher)
}

PathQueryConditionFactory - when you want to use common URL path prefix multiple times:

fun factory() {
    val dispatcher = FixtureDispatcher()
    val factory = PathQueryConditionFactory("/prefix/")
    dispatcher.putResponse(factory.withPathSuffix("suffix"), "queryless_response")
    // match all URLs with path ending with "suffix" and have "param" with any value as query parameter e.g. http://example.test/prefix/user/suffix?param
    dispatcher.putResponse(factory.withPathSuffixAndQueryParameter("suffix", "param"), "response_with_query_parameter")
    // match all URLs with path ending with "suffix" and have "param" with "value" as query parameter e.g. http://example.test/prefix/user/suffix?param=value
    dispatcher.putResponse(factory.withPathSuffixAndQueryParameter("suffix", "param", "value"), "response_with_query_parameter_and_value")
    // match all URLs with path ending with "suffix" and have multiple parameter name/value pairs e.g.http://example.test/prefix/user/suffix?param=value&param2=value2
    dispatcher.putResponse(
        factory.withPathSuffixAndQueryParameters("suffix", mapOf("param" to "value", "param2" to "value2")),
        "response_with_multiple_query_parameters"
    )
    mockWebServer.setDispatcher(dispatcher)
}

PathQueryCondition - when you want to match by path and optional query parameter:

fun pathQueryCondition() {
    val dispatcher = FixtureDispatcher()
    dispatcher.putResponse(PathQueryCondition("/prefix/suffix", "param", "value"), "response_with_query_parameter_and_value")
    mockWebServer.setDispatcher(dispatcher)
    
}

Also supports a map of multiple query parameters:

fun pathQueryConditions() {
    val dispatcher = FixtureDispatcher()
    dispatcher.putResponse(PathQueryCondition("/prefix/suffix", mapOf("param" to "value", "param2" to "value2")), "response_with_query_parameters_and_values")
    mockWebServer.setDispatcher(dispatcher)
}

HttpUrlCondition - when you want to match by some part of URL other than path or single query parameter:

fun httpUrlCondition() {
    val dispatcher = FixtureDispatcher()
    val condition = object : HttpUrlCondition() {
        override fun isUrlMatching(url: HttpUrl) = url.encodedUsername() == "foo"

        override fun compareTo(other: Condition) = 0
    }
    dispatcher.putResponse(condition , "response_for_foo")
    mockWebServer.setDispatcher(dispatcher)    
}

Condition - when you want to match by non-URL parts of the request e.g. headers:

fun condition() {
    val condition = object : Condition {
        override fun isRequestMatching(request: RecordedRequest)= request.getHeader("Content-Type") == "application/json"

        override fun compareTo(other: Condition) = 0
    }
    dispatcher.putResponse(condition , "json_response")   
}

Download

For unit tests:

testImplementation 'pl.droidsonroids.testing:mockwebserver-path-dispatcher:1.1.7'

or for Android instrumentation tests:

androidTestImplementation 'pl.droidsonroids.testing:mockwebserver-path-dispatcher:1.1.7'

License

Library uses the MIT License. See LICENSE file.

mockwebserver-path-dispatcher's People

Contributors

abcarrell avatar dmanluc avatar eduardbosch avatar koral-- avatar mszydlow avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

mockwebserver-path-dispatcher's Issues

How to specify a null body for a 204 response?

My YAML file for my 204 response is as follows:

statusCode : 204

I get the following exception during testing this:

03-18 17:59:04.679 12529-12660/com.iwsinc.gmi.test E/MockWebServer: MockWebServer[50000] connection from /127.0.0.1 crashed
    java.lang.IllegalArgumentException: Invalid path: fixtures/null
        at pl.droidsonroids.testing.mockwebserver.StringUtils.getResourceAsString(StringExtensions.kt:7)
        at pl.droidsonroids.testing.mockwebserver.YamlResourcesParser.parseFrom(YamlResourcesParser.kt:18)
        at pl.droidsonroids.testing.mockwebserver.MockResponseBuilder.buildMockResponse(MockResponseBuilder.kt:9)
        at pl.droidsonroids.testing.mockwebserver.FixtureDispatcher.dispatch(FixtureDispatcher.kt:44)
        at okhttp3.mockwebserver.MockWebServer$4.processOneRequest(MockWebServer.java:573)
        at okhttp3.mockwebserver.MockWebServer$4.processConnection(MockWebServer.java:531)
        at okhttp3.mockwebserver.MockWebServer$4.execute(MockWebServer.java:456)
        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)

...If I attempt to define an empty body with my YAML like this:

statusCode : 204
headers:
  - 'Content-Type: application/json; charset=utf-8'
body: >
 {}

... then I get this exception instead:

java.net.ProtocolException: HTTP 204 had non-zero Content-Length: 2

How do I define a simple 204 response with an empty body to satisfy HTTP protocol here, any ideas?

My dispatcher definition line for this particular response is as follows:

    dispatcher.putResponse(pathQueryConditionFactory.withPathSuffix("/endpoint"), "yaml_file")

Thank you for any insights or suggestions!

1.1.5 not available on Maven Central

Hi @koral--

I've been checking the 1.1.5 version but does not appear on maven central.
Do you know if there was any problem with the release?
Normally, it does not take much time to appear online.

Many thanks πŸ™‚

Multiple Query Parameters

So far, this library looks exactly like what I'm looking for when testing MockWebserver.

I have run into a problem with a request that has multiple query parameters.

For example:

@Before
fun onBefore() {
   val dispatcher = FixtureDispatcher()
   val factory = PathQueryConditionFactory()
   dispatcher.putResponse(factory.withPathSuffixAndQueryParameter("/some/suffix?parameter1=x", "parameter2", "y"), "mapped")
   mockWebServer.start(8080)
   mockWebServer.dispatcher = dispatcher

this causes an exception to be thrown by the mockwebserver:

E/MockWebServer: MockWebServer[8080] connection from /127.0.0.1 crashed
    java.lang.IllegalArgumentException: Unexpected request: GET /some/suffic?parameter1=x&parameter2=y HTTP/1.1
        at pl.droidsonroids.testing.mockwebserver.FixtureDispatcher.dispatch(FixtureDispatcher.kt:47)
        at okhttp3.mockwebserver.MockWebServer$SocketHandler.processOneRequest(MockWebServer.kt:593)
        at okhttp3.mockwebserver.MockWebServer$SocketHandler.handle(MockWebServer.kt:551)
        at okhttp3.mockwebserver.MockWebServer$serveConnection$$inlined$execute$1.run(Util.kt:556)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
        at java.lang.Thread.run(Thread.java:764)

It would be nice to be able to pass in a query map (like Retrofit). Are there plans for this?

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.