Kong is a popular API gateway we are proactively using. Sometimes we need to develop plugins to meet specific architecture/business needs.

It’s easy to follow the official guide plugin-development to write one. However, it could be tricky to do testing/mocking as there are not a lot of docs on that.

I happen to have some practice and had Q&A with the Kong team regarding how to mock; below is what I got, hope it helps who needs it.

Pongo setup

Pongokong-pongo is a plugin test tool, it runs your plugins against the dockerised Kong. While you can check the official Readme. Here are the set-up steps.

  1. install pongo -> https://github.com/Kong/kong-pongo#installation

  2. cd /path/to/this/project

  3. pongo init which gets you a new folder named .pongo

  4. edit ./pongo/pongorc if you need to change default parameters like –no-cassandra or –redis

  5. pongo run will run all unit tests under the folder /spec

Mock

It’s a good chance your plugin will call service from the cluster or 3rd party, thus you might need to mock these services because of network/data issues, and more importantly, you better have unit tests run on your CI when you are serious about your plugins.

Before we start the mocking, let’s recall how a Kong plugin works. Kong together with all its plugins is built on top of OpenResty(https://openresty.org/en/ ). By leveraging OpenResty(also Nginx), Kong is eventually deployed as an Nginx server.

With that in mind, if we can find a way to inject the Nginx server setting and set up a fake service, we would be able to mock any 3rd party services. So how do we get there?

It turns out pongo has offered such an example

you can get the example by typing pongo docs, then looking at shell helpers and check out the example at start_kong

I have attached it below

-- example mocks
-- Create a new DNS mock and add some DNS records
local fixtures = {
  http_mock = {},
  stream_mock = {},
  dns_mock = helpers.dns_mock.new()
}

fixtures.dns_mock:A {
  name = "a.my.srv.test.com",
  address = "127.0.0.1",
}

-- The blocks below will be rendered by the Kong template renderer, like other
-- custom Kong templates. Hence the ${{xxxx}} values.
-- Multiple mocks can be added each under their own filename ("my_server_block" below)
fixtures.http_mock.my_server_block = [[
     server {
         server_name my_server;
         listen 10001 ssl;

         ssl_certificate ${{SSL_CERT}};
         ssl_certificate_key ${{SSL_CERT_KEY}};
         ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;

         location ~ "/echobody" {
           content_by_lua_block {
             ngx.req.read_body()
             local echo = ngx.req.get_body_data()
             ngx.status = status
             ngx.header["Content-Length"] = #echo + 1
             ngx.say(echo)
           }
         }
     }
   ]]

If you look at fixtures.http_mock.my_server_block part. It will add another mock server to the same config. The block is eventually injected as a standard Nginx configuration. In this example, the directive listen 10001 ssl tells Nginx to listen on port 10001 for incoming ssl connections.

Therefore, In our tests, we can set up an actual service that points to this mock (in this case url=https://localhost:10001). we can define whatever HTTP status/content we need following this example.

local fixtures = {
    http_mock = {}
}

fixtures.http_mock.my_server_block = [[
     server {
        server_name my_server;

        listen 10001;

        location ~ "/any/path" {
            return 200;
        }
        
     }
   ]]


describe("account-store-auth [#" .. strategy .. "]", function()
    local proxy_client

    lazy_setup(function()
        local bp = helpers.get_db_utils(strategy, {"routes", "services", "plugins"}, {'account-store-auth'})


        local service = bp.services:insert{
            name="my_server",
            url="http://localhost:10001",
        }

        local route1 = bp.routes:insert{
            hosts = {"route1.org"},
            service = {id = service.id}
        }
...

References

This is a great article regarding how Kong works as an Nginx module

kong-源码分析

This is the discussion that leads to this How-to

https://github.com/Kong/kong-pongo/issues/292