Git Product home page Git Product logo

mockinizer's Introduction

Mockinizer Build Status

An OkHttpClient / RetroFit web api call mocking library that uses MockWebServer to provide mocked responses by using Interceptors.

What is it?

Mockinizer helps Android developers to build apps with web api calls that use OkHttpClient / RetroFit by allowing them to quickly mock some web api responses with MockWebServer. Mockinizer allows to mock only specific api calls, while other api calls will still call the original server. This is particularily usefull in the following scenarios:

  • You are working on a new feature that needs to call new not yet existing apis. With Mockinizer you can quickly swap in the new desired mocked api responses while other api calls will still use the real server
  • You want to test error cases for existing apis. With Mockinizer you can can quickly mock an error 500 response or an 401 Unauthorized for selected api requests and verify if your app handles them gracefully
  • You want to call a mocked api for unit testing and isolate those from the webserver

Setup

1. Add jitpack.io repository:

Add jitpack.io repository in root build.gradle:

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

2. Add Mockinizer gradle dependency

Add the below code in the app module's build.gradle (Usually you want to implement it only in debug builds and not release builds) At the time of writing the latest mockinizer_version was 1.1.0, you can get latest release version here: https://github.com/donfuxx/Mockinizer/releases

dependencies {
    debugImplementation "com.github.donfuxx:Mockinizer:1.6.0"
}

You may also need to add a MockWebServer dependency in your app module:

dependencies {
    implementation "com.squareup.okhttp3:mockwebserver:4.0.1"
}

3. Define the RequestFilter / MockResponse Pairs

Those represent each api call that you want to mock. The RequestFilter defines the Request Path (relative to the RetroFit Baseurl) and/or the json body of the request. The MockResponse is the desired Response that you want to get returned by your local MockWebServer. See a simple example that defines 2 mock responses where one out of them is an error:

package com.appham.mockinizer.demo

import com.appham.mockinizer.RequestFilter
import okhttp3.mockwebserver.MockResponse

val mocks: Map<RequestFilter, MockResponse> = mapOf(

    RequestFilter("/mocked") to MockResponse().apply {
        setResponseCode(200)
        setBody("""{"title": "Banana Mock"}""")
    },

    RequestFilter("/mockedError") to MockResponse().apply {
        setResponseCode(400)
    }

)

4. Add mockinizer to OkHttpClient

To wire up the mocks that you defined in step 3 you just have to call the mockinize(mocks) extension function in the OkHttpClient builder and provide the mocks map as the parameter, see example:

OkHttpClient.Builder()
            .addInterceptor(loggingInterceptor)
            .mockinize(mocks) // <-- just add this line
            .build()

Yes, that is it! Just add .mockinize(mocks) into the OkHttpClient.Builder chain. Happy mocking! :-)

5. Launch app and check logs to verify mocking is working

Once you call a mockinized api endpoint in your app then you can verify the mocked responses in the logcat. The attached HttpLogging interceptor should produce logs similar to:

D/OkHttp: --> GET https://my-json-server.typicode.com/typicode/demo/mockedError
D/OkHttp: --> END GET
D/OkHttp: --> GET https://my-json-server.typicode.com/typicode/demo/mocked
D/OkHttp: --> END GET
D/OkHttp: --> GET https://my-json-server.typicode.com/typicode/demo/posts
D/OkHttp: --> END GET
D/OkHttp: <-- 400 https://localhost:34567/typicode/demo/mockedError (97ms)
D/OkHttp: content-length: 0
D/OkHttp: mockinizer: <-- Real request /typicode/demo/mockedError is now mocked to HTTP/1.1 400 Client Error
D/OkHttp: server: Mockinizer 1.6.0 by Thomas Fuchs-Martin
D/OkHttp: <-- END HTTP (0-byte body)
D/OkHttp: <-- 200 https://localhost:34567/typicode/demo/mocked (104ms)
D/OkHttp: content-length: 98
D/OkHttp: mockinizer: <-- Real request /typicode/demo/mocked is now mocked to HTTP/1.1 200 OK
D/OkHttp: server: Mockinizer 1.6.0 by Thomas Fuchs-Martin
D/OkHttp: [
D/OkHttp:   {
D/OkHttp:     "id": 555,
D/OkHttp:     "title": "Banana Mock"
D/OkHttp:   },
D/OkHttp:   {
D/OkHttp:     "id": 675,
D/OkHttp:     "title": "foooo"
D/OkHttp:   }
D/OkHttp: ]
D/OkHttp: <-- END HTTP (98-byte body)
D/OkHttp: <-- 200 https://my-json-server.typicode.com/typicode/demo/posts (1416ms)
D/OkHttp: date: Sat, 28 Mar 2020 19:11:32 GMT
D/OkHttp: content-type: application/json; charset=utf-8
D/OkHttp: set-cookie: __cfduid=de53d4c305959e69c3e8ea1e6b78959001585422691; expires=Mon, 27-Apr-20 19:11:31 GMT; path=/; domain=.typicode.com; HttpOnly; SameSite=Lax
D/OkHttp: x-powered-by: Express
D/OkHttp: vary: Origin, Accept-Encoding
D/OkHttp: access-control-allow-credentials: true
D/OkHttp: cache-control: no-cache
D/OkHttp: pragma: no-cache
D/OkHttp: expires: -1
D/OkHttp: x-content-type-options: nosniff
D/OkHttp: etag: W/"86-YtXc+x6dfp/4aT8kTDdp4oV+9kU"
D/OkHttp: via: 1.1 vegur
D/OkHttp: cf-cache-status: DYNAMIC
D/OkHttp: expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
D/OkHttp: server: cloudflare
D/OkHttp: cf-ray: 57b3a8502b8a71f7-AMS
D/OkHttp: [
D/OkHttp:   {
D/OkHttp:     "id": 1,
D/OkHttp:     "title": "Post 1"
D/OkHttp:   },
D/OkHttp:   {
D/OkHttp:     "id": 2,
D/OkHttp:     "title": "Post 2"
D/OkHttp:   },
D/OkHttp:   {
D/OkHttp:     "id": 3,
D/OkHttp:     "title": "Post 3"
D/OkHttp:   }
D/OkHttp: ]
D/OkHttp: <-- END HTTP (134-byte body)

In above example three api calls have been made. The only api call that wasn´t mocked is the call to https://my-json-server.typicode.com/typicode/demo/posts the other calls to /mocked and /mockedError got swapped in by Mockinizer! (Notice localhost responding to those with the previously defined mock responses)

See Mockinizer Demo Project

In case you want to see how it works in action then just check out the Mockinizer demo project!

Run automated tests

Another good way to see Mockinizer in action is to run the androidTests: just run ./gradlew build connectedCheck from the terminal and see in the logcat how api calls got mocked!

Contributing

Pull requests are welcome! Just fork Mockinizer and add submit your PR. Also feel free to add issues for new feature requests and discovered bugs. Check the Contributing guidelines for more infos.

Feedback

If you like this project then please don't forget to stargaze Mockinizer and share it with your friends!

Stay up to date

Always be the first to know about new releases and add Mockinizer to your watches.

mockinizer's People

Contributors

donfuxx 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

mockinizer's Issues

Simultaneous queries can lead to the wrong responses

Describe the bug
I have two API-Endpoints, getA() and getB() which return ResultA and ResultB respectively. Both are mocked using Mockinizer. Both Endpoints are queried at the same time. In a good number of cases the getA() query will end up getting the ResultB response, and vice versa.

My guess is that both results are intercepted by Mockinizer, the responses are enqueued into MockServer, but MockServer handles the two responses in the opposide order, leading to the wrong response being given out.

I've verified that retrofit actually receives the wrong response by logging to json body of for the two endpoints.

To Reproduce
Very simply put, I have this set-up.

interface TestInterface {
    @GET("/v1/a")
    suspend fun getA(): Response<ResultA>
    @GET("/v1/b")
    suspend fun getB(): Response<ResultB>
}
class Repository {
    fun loadA() {
        GlobalScope.launch {
            val resultA = retrofit.createInterface(...).getA() // sometimes fails because my mock response for getB can't be converted to ResultA
        }
    }
    fun loadB() {
        GlobalScope.launch {
            val resultB = retrofit.createInterface(...).getB() // sometimes fails because my mock response for getA can't be converted to ResultB
        }
    }
}
fun someFunction() {
    repository.loadA()
    repository.loadB()
}

At the moment I'm trying to understand the issue and figure out if I'm misusing something or if Mockinizer+MockServer is the culprit. I'll see if I can create a minimal test sample to showcase the problem.

In the meantime, I thought I'd ask whether you've seen issues like that previously.

PS: Perhaps to add, in the past I've used this method to handle giving out mock responses, rather than enqueue. Maybe that's a good way to go.

Regression from 1.4.0: Requests with query parameters don't succeed anymore

Describe the bug
Thanks for you work on #25. I've tested it and it seems to be resolved.

However, it seemed to have introduced a regression. I have a mock for /path/endpoint that usually has a query parameter associated with it. That is, the OkHttp call is addressed to /path/endpoint?id=xyz. This used to be ignored by Mockinizer, meaning the call got successfully mocked.

Now, with 1.5.0, the call fails and results in a ' 404'. If I try to add a mock for /path/endpoint?id=xyz I'll get a No mocks found for Request error instead. It seems the decision whether the request should be mocked is made without taking into account the query parameters, whereas the decision which mock to use to done with them.

Expected behavior
This shows room for another enhancement of Mockinizer: being able to handle queries. I'd imagine this being done in a similar way to headers/body. E.g. one could write:

// Require "id" to be exactly "1234"
RequestFilter("/path/endpoint", method = Method.GET, queries = listOf(QueryRule("id", "1234")))

// Require "id" to be set to any value
RequestFilter("/path/endpoint", method = Method.GET, queries = listOf(QueryRule("id")))

// Succeeds regardless of any query parameters set
RequestFilter("/path/endpoint", method = Method.GET)

I'd be willing to implement this if you agree to the syntax and behavior.

How to return success or error based on body params

So I'm not sure if there is a way to do this or not, but in your examples, you have a success endpoint and a failure endpoint. In real life, there would be only 1 endpoint that returned success or failure.

So in my example, a user logs in and the body would be something like {"email":"[email protected]","password":"12345"} and they would get a token returned.

So my mock looks like this:

    RequestFilter("/v1/login", method = Method.POST) to MockResponse().apply {
        setResponseCode(200)
        setBody(
            """
                {
                "token":"000000000000000000000"
                } 
            """.trimIndent()
        )
        setBodyDelay(1, TimeUnit.SECONDS)
        setHeadersDelay(1, TimeUnit.SECONDS)
    }

Which is working fine. But what if I wanted to test a case where the login credentials are bad and therefore it returns an error? This way I can test what the UI looks like after the error response. Obviously, I would need to do this from the same endpoint.

So I would want setResponseCode to be conditional by somehow checking the request body

bind failed: EADDRINUSE (Address already in use)

Hi,

I added some mocks to my OkHttpClientBuilder and I'm getting this error. Is this a bug, or am I doing something wrong? I just create a typical OkHttpBuilder with some interceptors like this:

val cookieJar = PersistentCookieJar(SetCookieCache(), SharedPrefsCookiePersistor(context)) val okHttpClient = OkHttpClient.Builder() .addInterceptor { val request = it .request() .newBuilder() .addHeader("Content-Type", "application/json") .addHeader("Accept", "application/json") .addHeader("Accept-Language", Locale.getDefault().toLanguageTag()) .addHeader("Content-Language", Locale.getDefault().toLanguageTag()) .addHeader("X-App-Language", Locale.getDefault().language) .addHeader("appType", Constants.appType) it.proceed(request.build()) } .addInterceptor(hostInterceptor) .addInterceptor(ConnectivityInterceptor(context)) .connectTimeout(15, TimeUnit.SECONDS) .readTimeout(240, TimeUnit.SECONDS) .writeTimeout(90, TimeUnit.SECONDS) .cookieJar(cookieJar)

and then call

builder.mockinize(mocks)

My mocks look something like this:

val mocks: Map<RequestFilter, MockResponse> = mapOf( RequestFilter( "/endpoint", headers = Headers.headersOf( "Content-Type", "application/json", "Accept", "application/json", "Accept-Language", Locale.getDefault().toLanguageTag(), "Content-Language", Locale.getDefault().toLanguageTag(), "App-Type", "android" ) ) to MockResponse().apply { setResponseCode(200) setBodyDelay(1, TimeUnit.SECONDS) setHeadersDelay(1, TimeUnit.SECONDS) setBody( Develop.loadMockData( "mock_reservation_success.json" ) ) },...

the error I'm getting:

2020-06-03 14:17:30.695 25141-25384/com.pelicantravel.pelipecky E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-1 Process: com.pelicantravel.pelipecky, PID: 25141 java.net.BindException: bind failed: EADDRINUSE (Address already in use) at libcore.io.IoBridge.bind(IoBridge.java:104) at java.net.PlainSocketImpl.socketBind(PlainSocketImpl.java:149) at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:393) at java.net.ServerSocket.bind(ServerSocket.java:376) at okhttp3.mockwebserver.MockWebServer.start(MockWebServer.kt:388) at okhttp3.mockwebserver.MockWebServer.start(MockWebServer.kt:370) at okhttp3.mockwebserver.MockWebServer.start(MockWebServer.kt:360) at com.appham.mockinizer.MockWebServerExtKt$configure$1.invokeSuspend(MockWebServerExt.kt:12) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56) at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665) Caused by: android.system.ErrnoException: bind failed: EADDRINUSE (Address already in use) at libcore.io.Linux.bind(Native Method) at libcore.io.ForwardingOs.bind(ForwardingOs.java:59) at libcore.io.IoBridge.bind(IoBridge.java:100) at java.net.PlainSocketImpl.socketBind(PlainSocketImpl.java:149)  at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:393)  at java.net.ServerSocket.bind(ServerSocket.java:376)  at okhttp3.mockwebserver.MockWebServer.start(MockWebServer.kt:388)  at okhttp3.mockwebserver.MockWebServer.start(MockWebServer.kt:370)  at okhttp3.mockwebserver.MockWebServer.start(MockWebServer.kt:360)  at com.appham.mockinizer.MockWebServerExtKt$configure$1.invokeSuspend(MockWebServerExt.kt:12)  at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)  at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)  at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)  at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)  at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)  at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665) 

Fix setting of mockserver port in buildconfig

02-02 20:23:36.607 E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-2
Process: com.appham.mockinizer.demo, PID: 18412
java.lang.NoSuchFieldError: No static field MOCKSERVER_PORT of type Ljava/lang/Integer; in class Lcom/appham/mockinizer/BuildConfig; or its superclasses (declaration of 'com.appham.mockinizer.BuildConfig' appears in /data/app/com.appham.mockinizer.demo-2/base.apk)
at com.appham.mockinizer.MockWebServerExtKt$configure$1.invokeSuspend(MockWebServerExt.kt:12)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)

Mockinizer headers are added several times

Describe the bug
Every time I query the same mocked API endpoint, a new set of headers are added to the mock response.

To Reproduce
Repeatedly query the same endpoint. Check the response, for example via the HttpLoggingInterceptor with logging level BODY.

Expected Behavior
The headers should be added only once, on creation of the MockResponse. Or the MockResponse needs to be cloned before being modified.

More info

image

This is a screenshot from the debugger after I've made a few requests. As you can see, the headersBuilder contains the same set of headers 3+ times.

Min SDK version

Hi,

Is there any reason to keep minSdkVersion at 21? I think it could be lowered to 16 so more people can use it.

Allow mocking without having to specify a body

Is your feature request related to a problem? Please describe.
I'm currently trying to mock a login POST request that takes a username and password and returns some token.
But if I understand the workings of Mockinizer correctly, I can only mock that request if I provide a body (e.g. a username and password) in the RequestFilter. If I don't, the body of the RequestFilter will never be equal to the body of the actual request, thus the call never never be mocked.

However, that makes it practically unusable for something like a login request since the user can enter any name and password they wish.

Describe the solution you'd like
An option to make Mockinizer ignore the request body (such as body = BODY.ANY) in order to catch all requests made to that endpoint would be great.
Even better if Mockinizer were clever enough to find better matches if they exist and fall back to a BODY.ANY RequestFilter.

Thank you for this library. It's convenient and simple. For now I'll work around this issue by explicitly setting username and password to predefined values.

Mockinizer not working with UI Tests using OkHttpIdlingResourceRule

Describe the bug
I have UI tests and I use OkHttpIdlingResourceRule() as a rule for my tests. The tests either don't use the mocks or terminates depending on how I set it up.

To Reproduce
My Retrofit Client
`

@Provides
@Reusable
@JvmStatic
internal fun provideRetrofitInterface(): Retrofit {
    val interceptor = HttpLoggingInterceptor()
    interceptor.level = HttpLoggingInterceptor.Level.BODY
    val client = OkHttpProvider.client.addInterceptor(interceptor).build()
    return Retrofit.Builder()
        .baseUrl(BASE_URL)
        .client(client)
        .addConverterFactory(MoshiConverterFactory.create())
        .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
        .build()
}

object OkHttpProvider {
    val client = OkHttpClient.Builder()
}

`

I need the object for the OkHttpProvider so that it can be used for the test rule :

class OkHttpIdlingResourceRule: TestRule {

private val resource : IdlingResource = OkHttp3IdlingResource.create("okhttp", 
 NetworkModule.OkHttpProvider.client.build())

    override fun apply(base: Statement, description: Description): Statement {
        return object: Statement() {
            override fun evaluate() {
                IdlingRegistry.getInstance().register(resource)
                base.evaluate()
                IdlingRegistry.getInstance().unregister(resource)
            }
        }
    }
}

And in my test:

@get:Rule
var rule = OkHttpIdlingResourceRule()

Everything works fine until I try to add mockinizer.

val mocks: Map<RequestFilter, MockResponse> = mapOf(

    RequestFilter("login") to MockResponse().apply {
        setResponseCode(200)
        setBody(
            """
                {
                "token": 1111111"
                } 
            """.trimIndent()
        )
        setBodyDelay(1, TimeUnit.SECONDS)
        setHeadersDelay(1, TimeUnit.SECONDS)
    }
)

When I add the .mockinize(mocks) to to the object OkHttpProvider the tests run but do not use the mock results:

    object OkHttpProvider {
        val client = OkHttpClient.Builder().mockinize(mocks)
    }

Instead, if I remove that and I add .mockinize(mocks) to the provideRetrofitInterface() I my tests fail:

    internal fun provideRetrofitInterface(): Retrofit {
        val interceptor = HttpLoggingInterceptor()
        interceptor.level = HttpLoggingInterceptor.Level.BODY
        val client = OkHttpProvider.client.addInterceptor(interceptor).mockinize(mocks).build()
        return Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(client)
            .addConverterFactory(MoshiConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
            .build()
    }

I get an error:

java.net.BindException: bind failed: EADDRINUSE (Address already in use)

I think it has something to do with the rule which is why I shared the code. But I'm not sure what the issue is.

Remote android.util.Log references to use this library in JUnit tests without Robolectric

Is your feature request related to a problem? Please describe.
Mockinizer internally uses android.util.log in DebugLogger which makes it impossible to use this library in JUnit tests without using Robolectric.

Describe the solution you'd like
Remove android.util.Log references from this library.

Describe alternatives you've considered
There is an option to provide a custom logger, but this provision is not there for all the places where DebugLogger is used.
mockinize method accepts a custom logger and I passed one there.
Still I got this error on using this library in JUnit test.

Method d in android.util.Log not mocked. See http://g.co/androidstudio/not-mocked for details.
java.lang.RuntimeException: Method d in android.util.Log not mocked. See http://g.co/androidstudio/not-mocked for details.
	at android.util.Log.d(Log.java)
	at com.appham.mockinizer.DebugLogger.d(Logger.kt:7)
	at com.appham.mockinizer.RequestFilter$Companion.from(RequestFilter.kt:36)
	at com.appham.mockinizer.MockinizerInterceptor$intercept$1.invoke(MockinizerInterceptor.kt:20)
	at com.appham.mockinizer.MockinizerInterceptor$intercept$2.invoke(MockinizerInterceptor.kt:42)
	at com.appham.mockinizer.MockinizerInterceptor.intercept(MockinizerInterceptor.kt:56)

MockinizerInterceptor instance is created internally and it uses DebugLogger and there is no way to provide a custom logger there as the dependencies required to create this interceptor are not public.

A simple solution would be to use println instead of android.util.log in DebugLogger.

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.