Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

io.github.infeez.kotlin-mock-server:mock-server-okhttp

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

io.github.infeez.kotlin-mock-server:mock-server-okhttp

Mock server by okhttp server

  • 1.0.3
  • Source
  • Maven
  • Socket score

Version published
Maintainers
1
Source

Release build status Maven Central kotlin-mock-server Join the chat

About The Project

The library for a simple mocking HTTP server using a URL condition of any complexity.

Here's why:

  • For quick application testing
  • Easy to understand without deep knowledge of Kotlin
  • Kotlin-DSL for mocking

Main features:

  • Starting a server for mocking http requests
  • Creating mock parameters using dsl context
  • Ability to combine parameters using and \ or constructs for a more flexible condition
  • Mock condition created by path, query, body, combining in any way
  • Mocking using direct url specification
  • Mock response parameters: response delay, response body, headers, response code
  • Ability to create your own mock server (netty(experimental) and okhttp are available)
  • Ability to replace, remove, add mock during test running
  • Ability to replace mock response parameters during test running
  • JUnit4 Rule support for the server lifecycle.
  • JUnit5 Support (Planned).

Getting Started

Installation

repositories {
    mavenCentral()
}
Kotlin mock server core

Gradle Groovy DSL

implementation 'io.github.infeez.kotlin-mock-server:mock-server-core:1.0.0'

Gradle Kotlin DSL

implementation("io.github.infeez.kotlin-mock-server:mock-server-core:1.0.0")
Kotlin mock server by okhttp

Gradle Groovy DSL

implementation 'io.github.infeez.kotlin-mock-server:mock-server-okhttp:1.0.0'

Gradle Kotlin DSL

implementation("io.github.infeez.kotlin-mock-server:mock-server-okhttp:1.0.0")
Kotlin mock server extension for jUnit4

Gradle Groovy DSL

implementation 'io.github.infeez.kotlin-mock-server:mock-server-junit4:1.0.0'

Gradle Kotlin DSL

implementation("io.github.infeez.kotlin-mock-server:mock-server-junit4:1.0.0")

Usage

Ways for create server and usage

Using rule for create and start server in your test class. Using OkHttp as a sample:

val mockServer = okHttpMockServer()

you can define mock inside as many times as you like okHttpMockServer:

val mockServer = okHttpMockServer {
    mock("/rule/okhttp/test") {
        body("its ok")
    }
    mock("/rule/okhttp/test") {
        body("its ok")
    }
}

In other place you can use mockServer with mock or mocks functions.

mockServer.mock("url") {}

mockServer.mocks {
    mock("url1") {}
    mock("url2") {}
}

You can use Rule JUnit4 to manage server lifecycle. You need to implement mock-server-junit4 (See Installation page) for use and just add .asRule() like this:

private val mockServer = okHttpMockServer()

@get:Rule
val mockServerRule = mockServer.asRule()

For create custom server you need to inheritance Server abstract class. TBD

MockServer configuration

The Mock server has two configurations.
The first contains network information and set when the server starts.
The second configuration used to configure the mocks.
First ServerConfiguration sets when server created:

val mockServer = okHttpMockServer(ServerConfiguration.custom {
    host = "localhost"
    port = 8888
})

ServerConfiguration.custom block have host and port. Sets the host and port on which the server will run. Make sure the port is not bind!
By default server used ServerConfiguration.default(). In it the host is the localhost, and the port is any unbinded from 50013 to 65535.

Second configuration singleton MockConfiguration contains converterFactory and defaultResponse.

val mockServer = okHttpMockServer(ServerConfiguration.custom {
    host = "localhost"
    port = 8888
}, {
    converterFactory = object : ConverterFactory {
        override fun <T> from(value: String, type: Type): T {
            TODO("Not yet implemented")
        }

        override fun <T> to(value: T): String {
            TODO("Not yet implemented")
        }
    }
    defaultResponse = MockWebResponse(404, body = "Mock not found!")
})

converterFactory - needed to parse a string into a model when matching the body of a request. Use the same method as in your project. Gson example:

private val gsonConverterFactory = object : ConverterFactory {
    private val gson = Gson()
    override fun <T> from(value: String, type: Type): T {
        return gson.fromJson(value, type)
    }

    override fun <T> to(value: T): String {
        return gson.toJson(value)
    }
}

defaultResponse - the default response if no mock is found when the client request for it. You can set any default response. By default MockWebResponse(404).

Direct mock

Direct mock is simple way to define condition with URL. Mock server will be equal url as it is with url in your application client.

val mock = mock("this/called/in/your/client") {
    body("response body")
}

Matcher mock

Matcher mock a way to create combination by path, header, query and body:

Path matcher

Sample for path equal /mock/url using eq matcher

val mock = mock {
    path { eq("/mock/url") }
} on { 
    body("response body")
}
Header matcher

Sample for header Content-Type: text/html; charset=utf-8 using eq matcher

val mock = mock {
    header("Content-Type") { eq("text/html; charset=utf-8") }
} on { 
    body("response body")
}
Query matcher

Sample for query /mock/url?param1=somevalue using eq matcher

val mock = mock {
    query("param1") { eq("somevalue") }
} on { 
    body("response body")
}
Body matcher

Sample for body as json {"name1":"value1","name2":2} using eq matcher

val mock = mock {
    body { eq("""{"name1":"value1","name2":2}""") }
} on { 
    body("response body")
}

Remember the eq matcher compares strings as is! If you need to compare the body, use the bodyEq matcher. This matcher converts the string into a model and compares the fields.
Body will be converted using ConverterFactory. See more in the Configuration chapter.

val mock = mock {
    body { bodyEq<YourBodyModel>("""{ "name1" : "value1" , "name2" : 2}""") }
} on { 
    body("response body")
}

If you need to compare each field in the body individually, use bodyMarch matcher. Json in body request:

{ 
    "name1" : "value1" , 
    "name2" : 2
}

mock for this body with bodyMarch:

val mock = mock {
    body { 
        bodyMarch<YourBodyModel> {
            name1 == "value1" && name2 == 2
        }
    }
} on { 
    body("response body")
}

of course the YourBodyModel must be defined in your project for a proper comparison:

data class YourBodyModel(val name1: String, val name2: Int)
Other matchers

You can also use any of the matchers: any,eq, startWith,endsWith,matches and create any combination. Like this:

val mock = mock {
    path { startWith("/mock") }
} on { 
    body("response body")
}

any - triggers on any value
eq - triggers when value in mock and value in client are equal
startWith - triggers when client value starts with value in mock
endsWith - triggers when client value ends with value in mock
matches - triggers when value in client passed by regex value in mock

To combine matchers, you have to use: and,or. Like this:

val mock = mock {
    path { startWith("/mock") } and path { endWith("url") } or path { eq("/mock/url") }
} on { 
    body("response body")
}

and combine other request parameters:

val mock = mock {
    path { 
        startWith("/mock/url") 
    } and header("Content-Type") { 
        eq("text/html; charset=utf-8") 
    } and query("param1") { 
        endsWith("lue") 
    } and body {
        bodyMarch<YourBodyModel> {
            name1 == "value1" && name2 == 2
        }
    }
} on { 
    body("response body")
}

Mock response parameters

The mock response may contain: HTTP-code, headers, body, delay.

val mock = mock { path { eq("/mock/url") } } on {
    code(200)
    headers {
        "name" to "value"
    }
    delay(1L, TimeUnit.SECONDS)
    body("response body")
    emptyBody()
}

In code(200) you can specify any HTTP-code.
Block headers takes key/value pair used infix fun to. You can also add the header using vararg method headers("name1" to "value1", "name2" to "value2") The answer can be delayed by the method delay(1000L) without the second argument because it's optional. The default is milliseconds. But you can use this method with two arguments and set the desired TimeUnit delay(1L, TimeUnit.MINUTES).
body(String|InputStream|File) method sets the body of the answer. You can use body overloading by specifying String, InputStream or File.
emptyBody() just sets body is empty.

Add mock to server

Remember! After you create a mock, you must add it to the mock server list!

val mock1 = mock { path { eq("/url1") } } on { body("response body1") }
val mock2 = mock { path { eq("/url2") } } on { body("response body2") }
val mock3 = mock { path { eq("/url3") } } on { body("response body3") }

mockServer.add(mock1)
mockServer.add(mock2)
mockServer.add(mock3)

// or

mockServer.addAll(mock1, mock2, mock3)

This may be necessary for different application scenarios. For example:

val login = mock { path { eq("/login") } } on { body("""{ "login":"userLogin", "password":"userPassword" }""") }
val getUserInfo = mock { path { eq("/userInfo") } } on { body("""{ "userName":"userName", "userSurname":"userSurname" }""") }
val userLoginTestScenario = listOf(login, getUserInfo)

mockServer.addAll(userLoginTestScenario)

or like this:

val userLoginTestScenario = mocks {
    mock { path { eq("/login") } } on { body("""{ "login":"userLogin", "password":"userPassword" }""") }
    mock { path { eq("/userInfo") } } on { body("""{ "id":1, "userName":"userName", "userSurname":"userSurname" }""") }
}

mockServer.addAll(userLoginTestScenario)

Management mocks in mockServer runtime

While the test is running, you can add, replace, change and remove mock. In some test case you may need it. Test's for example:

Add new mock in runtime
private val mockServer = okHttpMockServer()

@get:Rule
val mockServerRule = mockServer.asRule()

@Test
fun `your awesome test need to add new mock`() {
    // some steps test
    // time to need add new mock while test running
    mockServer.mock("url") {}
    // now mock with "url" available for test
}
Replace mock in runtime
class LoginTestCase {

    private val mockServer = okHttpMockServer {
        add(LoginMocks.loginByUserJohn)
    }

    @get:Rule
    val mockServerRule = mockServer.asRule()

    @Test
    fun `your awesome test need to replace mock by other mock`() {
        // some steps test
        // some steps test with loginByUserJohn data
        // now you test case need to change mock. It may be necessary for another test case
        mockServer.replace(LoginMocks.loginByUserJohn, LoginMocks.loginByUserPeter)
        // some steps test with loginByUserPeter data
    }
}

object LoginMocks {
    val loginByUserJohn = mock("url/login") {
        body("""{ "username" : "John", "password": "123" }""")
    }
    
    val loginByUserPeter = mock("url/login") {
        body("""{ "username" : "Peter", "password": "456" }""")
    }
}
Change mock response in runtime
class LoginTestCase {

    private val mockServer = okHttpMockServer {
        add(LoginMocks.loginByUserJohn)
    }

    @get:Rule
    val mockServerRule = mockServer.asRule()

    @Test
    fun `your awesome test need to change mock`() {
        // login test case
        // now you need to check user not found when login
        // you can change login response to code 404
        LoginMocks.loginJohnAsAdmin.change<User> {
            code(404)
        }
        // user not found test case
    }
}

object LoginMocks {

    val loginByUserJohn = mock("url/login") {
        body("""{ "username" : "John", "password": "123" }""")
    }
}
Remove mock in runtime
class LoginTestCase {

    private val mockServer = okHttpMockServer {
        addAll(LoginMocks.loginTestCase)
    }

    @get:Rule
    val mockServerRule = mockServer.asRule()

    @Test
    fun `your awesome test need to remove mock`() {
        // some steps test with loginByUserJohn and this user is ADMIN is this test case
        // now you test case need to remove userDataIfUserAdmin for test when John is not admin 
        // and request "url/roles?user=john" no needed
        mockServer.remove(LoginMocks.userDataIfUserAdmin)
        // now "url/roles?user=john" not available for test
    }
}

object LoginMocks {
    
    val loginByUserJohn = mock("url/login") {
        body("""{ "username" : "John", "password": "123" }""")
    }
    
    val userDataIfUserAdmin = mock("url/roles?user=john") {
        body("""{ "userRole" : "ADMIN" }""")
    }

    val loginTestCase = mocks {
        +loginByUserJohn
        +userDataIfUserAdmin
    }
}

Mocks declaration

I recommend declare the mock lists for the test case in separate object classes. object LoginMocks, object UserMocks, object NewsMocks - contain a list of mocks by test case.

License

Distributed under the Apache License 2.0. See LICENSE for more information.

Contact

Vadim Vasyanin - infeez@gmail.com

FAQs

Package last updated on 01 Dec 2023

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc