Newly Purchased Memories

    server-mockr
    TypeScript icon, indicating that this package has built-in type declarations

    1.1.1 • Public • Published

    Server Mockr

    Mock HTTP APIs for rapid development and reliable testing.

    Table of Contents

    How does it work?

    When Server Mockr receives a request it matches the request against all active scenarios and expectations that have been configured. It will verify the request if needed and respond as configured in the expectation. When no matching expectation is found, the mock server will return a 404.

    Server Mockr will spin up two servers on startup: the mock server and the control server. The control server has a GUI and a REST API that can be used to control the mock server. It can be used to view, start and stop scenarios or retrieve request logs.

    Install

    $ npm install server-mockr --save-dev

    Usage

    You can setup a mock server like this:

    const { ServerMockr } = require("server-mockr");
    
    const mockr = new ServerMockr();
    mockr.when("/todos/1").respond({ id: 1, completed: true });
    mockr.start();

    This setup says that we will match every HTTP call to /todos/1 and respond with a status 200 JSON response.

    By default the mock server is available at http://localhost:3001 and the control server at http://localhost:3002.

    Specifying requests

    Request path

    Using a string:

    mockr.when("/resource").respond("ok");

    Using a string with parameters:

    mockr.when("/resources/:id").respond("ok");

    Using a regular expression:

    mockr.when(/\.html$/).respond("ok");

    Using a request matcher:

    mockr.when(request("/resource")).respond("ok");

    Using the path method on a request matcher:

    mockr.when(request().path("/resource")).respond("ok");

    Using the path method on a request matcher with a value matcher:

    mockr.when(request().path(startsWith("/res"))).respond("ok");

    Using the path method on a request matcher with a custom value matcher:

    mockr.when(request().path(path => path.includes("todos"))).respond("ok");

    Request params

    Request path parameters can be matched by using the param method:

    mockr.when(request("/resources/:id").param("id", "1")).respond("ok");

    Request method

    The request method can be specified by using the method method or the with the get,post,put,delete shortcut methods.

    Using the method method:

    mockr.when(request("/resource").method("POST")).respond("ok");

    Using a shortcut method:

    mockr.when(request().post("/resource")).respond("ok");

    Request body

    The request body can be specified by using the body method.

    Body with exact matcher:

    mockr
      .when(
        request()
          .post("/resources")
          .body({ firstName: "First", lastName: "Last" })
      )
      .respond({ id: 1, firstName: "First", lastName: "Last" });

    Body with partial matcher:

    mockr
      .when(
        request()
          .post("/resources")
          .body(matchesObject({ firstName: "First" }))
      )
      .respond({ id: 1, firstName: "First", lastName: "Last" });

    Body with property matcher:

    mockr
      .when(
        request()
          .post("/resources")
          .body(prop("firstName", startsWith("F")))
      )
      .respond({ id: 1, firstName: "First", lastName: "Last" });

    Body with multiple matchers:

    mockr
      .when(
        request()
          .post("/resources")
          .body(prop("firstName", startsWith("F")))
          .body(prop("lastName", startsWith("L")))
      )
      .respond({ id: 1, firstName: "First", lastName: "Last" });

    Body with custom matcher:

    mockr
      .when(
        request()
          .post("/resources")
          .body(body => body.firstName.startsWith("F"))
      )
      .respond({ id: 1, firstName: "First", lastName: "Last" });

    Request query string

    The request query string can be specified by using the query method.

    Query string with single parameter (/resources?limit=100):

    mockr
      .when(
        request()
          .get("/resources")
          .query("limit", "100")
      )
      .respond("ok");

    Query string with array parameter (/resources?id=1&id=2):

    mockr
      .when(
        request()
          .get("/resources")
          .query("id", ["1", "2"])
      )
      .respond("ok");

    Query string with multiple parameters (/resources?limit=100&order=asc):

    mockr
      .when(
        request()
          .get("/resources")
          .query("limit", "100")
          .query("order", "asc")
      )
      .respond("ok");

    Query string with multiple paramters and matchers (/resources?limit=99&order=asc):

    mockr
      .when(
        request()
          .get("/resources")
          .query("limit", matchesRegex(/[0-9]+/))
          .query("order", anyOf("asc", "desc"))
      )
      .respond("ok");

    Query string with custom matcher:

    mockr
      .when(
        request()
          .get("/resources")
          .query(query => query.limit === "100")
      )
      .respond("ok");

    Request headers

    Request headers can be specified by using the header method.

    Single header:

    mockr
      .when(
        request()
          .get("/resources")
          .header("Authorization", "token")
      )
      .respond("ok");

    Multiple headers:

    mockr
      .when(
        request()
          .get("/resources")
          .header("Authorization", "token")
          .header("Accept-Language", includes("nl-NL"))
      )
      .respond("ok");

    Request cookies

    Cookies can be specified by using the cookie method.

    mockr
      .when(
        request()
          .get("/resources")
          .cookie("sessionId", "id")
      )
      .respond("ok");

    Request files

    Files can be specified by using the file method.

    mockr
      .when(
        request()
          .get("/resources")
          .file("image", {
            fileName: "image.png",
            mimeType: "image/png",
            size: 144
          })
      )
      .respond("ok");

    Request url

    The url method allows you to match on the exact url:

    mockr.when(request().url("/resources?limit=100&order=asc")).respond("ok");

    Specifying responses

    Response status

    Using the respond method to specify the response status code:

    mockr.when("/resource").respond(404);

    Using the respond method to specify the response status code and body:

    mockr.when("/resource").respond(404, "Not Found");

    Use the status method to specify the response status code:

    mockr.when("/resource").respond(response().status(404));

    Response body

    Using a string, will respond with a status 200 text response:

    mockr.when("/resource").respond("ok");

    Using an object, will respond with a status 200 JSON response:

    mockr.when("/resource").respond({ id: 1 });

    Using a response builder, will respond with a status 200 text response:

    mockr.when("/resource").respond(response("match"));

    Using a response builder with the body method:

    mockr.when("/resource").respond(response().body("match"));

    Using a custom function, this can be useful to inject request values:

    mockr
      .when("/resources/:id")
      .respond(({ req }) => response({ id: req.params.id });

    Using a promise:

    mockr
      .when("/resources/:id")
      .respond(({ req }) => {
        const resource = await fetchResource(req.params.id);
        return response(resource);
      });

    Response headers

    Use the header method to specify response headers:

    mockr
      .when("/resource")
      .respond(response("ok").header("Cache-Control", "no-cache"));

    Response cookies

    Use the cookie method to specify response cookies:

    mockr.when("/resource").respond(response("ok").cookie("sessionId", "ID"));

    Cookie options can be set with an additional argument:

    mockr
      .when("/resource")
      .respond(response("ok").cookie("sessionId", "ID", { httpOnly: true }));

    Response delay

    Use the delay method to delay a response in miliseconds:

    mockr.when("/resource").respond(response("ok").delay(1000));

    Use the second argument to specify a minimum and maximum delay:

    mockr.when("/resource").respond(response("ok").delay(1000, 2000));

    Response redirect

    Use the redirect method specify a 302 redirect:

    mockr.when("/resource").respond(response().redirect("/new-resource"));

    Response proxy

    Use the proxy method to proxy requests to a real server:

    mockr
      .when(request().url(startsWith("/some-api")))
      .respond(response().proxy("https://example.com"));

    Use the proxyRequest builder to override request values:

    mockr
      .when("/some-api/test")
      .respond(
        response().proxy(
          proxyRequest("https://example.com").path("/other-api/test")
        )
      );

    Specifying times

    Use the times method to specify how many times an expectation should match:

    mockr
      .when("/resource")
      .times(1)
      .respond("First time");
    
    mockr
      .when("/resource")
      .times(1)
      .respond("Second time");

    Specifying behaviours for all responses

    It is possible to set response behaviours for all responses using the next method.

    When calling the next method, the expectation will not respond. Instead, it will set the specified response behaviour and proceed to the next matching expectation.

    mockr
      .when("*")
      .respond(response().header("Access-Control-Allow-Origin", "*"))
      .next();
    
    mockr.when("/resource").respond("match with cors");

    Verifying requests

    Using the verify method, it is possible to verify if a matched request, matches certain conditions. By default, the mock server will return a status 400 JSON response containing the validation error. It is possible to override the default response with the verifyFailedRespond method.

    Default verify, will respond with a status 400 JSON response containing the verification error:

    mockr
      .when(request().post("/resources"))
      .verify(request().body({ firstName: "First" }))
      .respond("ok");

    Verify with custom response:

    mockr
      .when(request().post("/resources"))
      .verify(request().body({ firstName: "First" }))
      .verifyFailedRespond(response("Server Error").status(500))
      .respond("ok");

    Conditional verification:

    mockr
      .when(request().post("/resources"))
      .verify(({ req }) =>
        req.headers["no-validate"] ? true : request().body({ firstName: "First" })
      )
      .respond("ok");

    Actions

    Actions are actions that can be taken after the mock server responded to some expectation.

    setState action

    The setState action can be used to change state. This can be useful to simulate stateful web services.

    // Respond with empty todos list
    mockr.when(request().get("/todos"), state("todos", undefined)).respond([]);
    
    // Respond with filled todos list
    mockr
      .when(request().get("/todos"), state("todos", "saved"))
      .respond([{ id: 1 }]);
    
    // Set todos to "saved"
    mockr
      .when(request().post("/todos"))
      .respond({ id: 1 })
      .afterRespond(setState("todos", "saved"));

    sendRequest action

    The sendRequest action can be used to trigger a HTTP request. This can be useful to simulate webhooks.

    scenario
      .when(request().post("/todos"))
      .respond({ id: 1 })
      .afterRespond(
        sendRequest("https://example.com/webhook")
          .header("X-Signature", "f9e91a6f0462b4c61e3667a4f4a6e7d02edfa518")
          .body({ action: "addedPost", post: { id: 1 } })
      );

    delay action

    The delay action can be used to delay other actions. This can be useful to trigger a webhook after some amount of time.

    scenario
      .when(request().post("/todos"))
      .respond({ id: 1 })
      .afterRespond(
        delay(
          sendRequest("https://example.com/webhook")
            .header("X-Signature", "f9e91a6f0462b4c61e3667a4f4a6e7d02edfa518")
            .body({ action: "addedPost", post: { id: 1 } }),
          5000
        )
      );

    Scenarios

    Scenarios can be used to group expectations together. They can be started and stopped programmatically, with the GUI or with the REST API. Scenarios also contain individual state, which is useful when simulating stateful web services. When a scenario is started, a scenario runner is created. A scenario can have multiple runners if the multipleScenarioRunners option is set to true.

    const scenario = mockr.scenario("todo-scenario");
    
    // Respond with empty todos list
    scenario.when(request().get("/todos"), state("todos", undefined)).respond([]);
    
    // Set todos to "saved"
    scenario
      .when(request().post("/todos"))
      .respond({ id: 1 })
      .afterRespond(setState("todos", "saved"));
    
    // Respond with todos list
    scenario
      .when(request().get("/todos"), state("todos", "saved"))
      .respond([{ id: 1 }]);

    Starting scenarios

    A scenario can be started in a few different ways.

    Using the GUI:

    Open a browser and navigate to the control server (by default http://localhost:3001). Click on the start button to start a scenario.

    Using the REST API:

    POST http://localhost:3001/api/scenarios/{scenarioID}/scenario-runners
    

    Using the REST API with default state:

    POST http://localhost:3001/api/scenarios/{scenarioID}/scenario-runners?state[todos]=saved
    

    Using the JS API:

    mockr
      .scenario("todo-scenario")
      .when(request().get("/todos"))
      .respond([]);
    
    mockr.scenarioRunner("todo-scenario");

    Using the JS API with default state:

    mockr
      .scenario("todo-scenario")
      .when(request().get("/todos"), state("todos", "saved"))
      .respond([{ id: 1 }]);
    
    mockr.scenarioRunner("todo-scenario", { state: { todos: "saved" } });

    Configurable scenarios

    Scenarios can be dynamically configured on startup with the onStart callback.

    This can be useful to create data on the fly or to conditionally add expectations based on some state.

    mockr
      .scenario("user-scenario")
      .config("userId", {
        type: "string",
        default: "000-000-000-001"
      })
      .onStart(({ config, scenario }) => {
        const user = createUser(config.userId);
        scenario.when(request().get("/user")).respond(user);
      });

    Bootstrapping

    Bootstrapping can be used to bootstrap a client.

    This can be useful if you for example want to redirect a browser to the application under test with certain parameters:

    mockr
      .scenario("todo-scenario")
      .onBootstrap(ctx =>
        response().redirect(`https://example.com/${ctx.config.locale}/todos`)
      )
      .when(request().get("/todos"))
      .respond([{ id: 1 }]);

    The client can then be bootstrapped by navigating the client to the following address:

    GET http://localhost:3001/api/scenarios/{scenarioID}/scenario-runners/create-and-bootstrap?config[locale]=nl-nl
    

    Matchers

    Matchers are functions which can be used to match values.

    allOf

    The allOf matcher can be used to check if some value matches all given matchers:

    mockr
      .when(request().path(allOf(startsWith("/static"), endsWith(".png"))))
      .respond("ok");

    anyOf

    The anyOf matcher can be used to check if some value matches any given matcher:

    mockr
      .when(request().path(anyOf(endsWith(".jpg"), endsWith(".png"))))
      .respond("ok");

    Or when given values:

    mockr.when(request().query("order", anyOf("asc", "desc"))).respond("ok");

    endsWith

    The endsWith matcher can be used to check if a string ends with some suffix:

    mockr.when(request().path(endsWith(".html"))).respond("ok");

    includes

    The includes matcher can be used to check if a string includes some other string:

    mockr.when(request().path(includes("todo"))).respond("ok");

    isEqualTo

    The isEqualTo matcher can be used to check if a value is equal to some other value:

    mockr.when(request().path(isEqualTo("/todos"))).respond("ok");

    isGreaterThan

    The isGreaterThan matcher can be used to check if a number is greater than some other number:

    mockr.when(request().body(prop("count", isGreaterThan(5)))).respond("ok");

    isGreaterThanOrEqual

    The isGreaterThanOrEqual matcher can be used to check if a number is greater than or equal to some other number:

    mockr
      .when(request().body(prop("count", isGreaterThanOrEqual(5))))
      .respond("ok");

    isLowerThan

    The isLowerThan matcher can be used to check if a number is lower than some other number:

    mockr.when(request().body(prop("count", isLowerThan(5)))).respond("ok");

    isLowerThanOrEqual

    The isLowerThanOrEqual matcher can be used to check if a number is lower than or equal to some other number:

    mockr.when(request().body(prop("count", isLowerThanOrEqual(5)))).respond("ok");

    matchesObject

    The matchesObject matcher can be used to check if an object partially matches some other object:

    mockr.when(request().body(matchesObject({ id: 1 }))).respond("ok");

    matchesRegex

    The matchesRegex matcher can be used to check if a string matches some regex:

    mockr
      .when(request().header("Authorization", matchesRegex(/[a-z0-9]+/)))
      .respond("ok");

    not

    The not matcher can be used to negate other matchers:

    mockr.when(request().path(not(startsWith("/res")))).respond("ok");

    When given a value, it will check if the value is not equal to:

    mockr.when(request().path(not("/res"))).respond("ok");

    oneOf

    The oneOf matcher can be used to check if some value matches exactly one of the given matchers:

    mockr
      .when(request().path(oneOf(startsWith("/static"), endsWith(".png"))))
      .respond("ok");

    pointer

    The pointer matcher can be used to check if the value referenced by the pointer matches some matcher:

    mockr
      .when(request().body(pointer("/addresses/0/street", includes("Street"))))
      .respond("ok");

    prop

    The prop matcher can be used to check if a property value matches some matcher:

    mockr.when(request().body(prop("firstName", startsWith("F")))).respond("ok");

    startsWith

    The startsWith matcher can be used to check if a string starts with some prefix:

    mockr.when(request().path(startsWith("/res"))).respond("ok");

    Logging

    HAR

    All requests and responses are logged and can be retrieved as HTTP Archive / HAR.

    Using the JS API:

    const har = mockr.getHAR();

    Using the REST API:

    GET http://localhost:3001/api/logging/har
    

    Options

    The ServerMockr class accepts the following options:

    const { ServerMockr } = require("server-mockr");
    
    const mockr = new ServerMockr({
      controlServerPort: 6001,
      mockServerPort: 6002,
      multipleScenarioRunners: true,
      globals: { globalValue: "value" }
    });

    Install

    npm i server-mockr

    DownloadsWeekly Downloads

    849

    Version

    1.1.1

    License

    MIT

    Unpacked Size

    873 kB

    Total Files

    202

    Last publish

    Collaborators

    • boschni