moleculerjs / rfcs

Request-For-Comments collection for Moleculer microservices framework
https://moleculer.services
MIT License
2 stars 1 forks source link

Testing helpers #3

Open icebob opened 4 years ago

icebob commented 4 years ago

Summary

We should create some testing tools/helpers in order to be more easily write tests for Moleculer services. E.g. mocking dependent services, waiting for emitted events, mocking broker.calls...etc

Detailed design

Creating broker for tests

createBroker is a helper which creates ServiceBroker instance for testing. It disables logger, add test middlewares & can register local mock services.

Base broker options which merged the passed options

const { EventCatcher, MockingCalls } = require("moleculer").Testing;

{
    logger: false,
    test: true,
    middlewares: [
        EventCatcher,
        MockingCalls
    ]
}

Create a tester broker instance with default options

const { createBroker } = require("moleculer").Testing;

describe("Test", ()=> {

    const broker = createBroker();

    beforeAll(() => broker.start());
    afterAll(() => broker.stop());

    it("should create broker", () => {
        // Tests
    });
});

Create broker with custom broker options

const { createBroker } = require("moleculer").Testing;

describe("Test", ()=> {

    const broker = createBroker({
        nodeID: "node-100",
        metrics: {
            enabled: true
        }
    });

    beforeAll(() => broker.start());
    afterAll(() => broker.stop());

    it("should create broker", () => {
        // Tests
    });
});

Create broker with options & mocked services

const { createBroker } = require("moleculer").Testing;

describe("Test", ()=> {
    const postsListFn = jest.fn(async () => [1,2,3]);

    const broker = createBroker({
        nodeID: "node-100",
        metrics: {
            enabled: true
        }
    }, [
        {
            name: "posts",
            actions: {
                list: postsListFn
            }
        }
    ]);

    beforeAll(() => broker.start());
    afterAll(() => broker.stop());

    it("should create broker", async () => {
        const res = await broker.call("posts.list", { limit: 5 });

        expect(res).toEqual([1,2,3]);
        expect(postsListFn).toBeCalledTimes(1);
        const ctx = postsListFn.mock.calls[0][0];
        expect(ctx.params).toEqual({ limit: 5 });
    });
});

Collect & wait for events

Need a testing middleware which wraps the emit, broadcast & broadcastLocal methods & collect every call. Register some asserting methods into ServiceBroker instance.

Assertion methods

const { EventCatcher } = require("moleculer").Testing;

The createBroker register it as middleware automatically.

Wait for an emitted event

const { createBroker } = require("moleculer").Testing;

describe("Test", ()=> {
    const postsListFn = jest.fn(async () => [1,2,3]);

    const broker = createBroker();

    broker.createService({
        name: "posts",

        events: {
            async "posts.addedComment"(ctx) {
                // Do something
                await Promise.delay(1000);

                ctx.emit("posts.updated", { postID: ctx.params.postID });
            }
        }
    })

    beforeAll(() => broker.start());
    afterAll(() => broker.stop());

    it("should emit 'posts.updated'", async () => {
        broker.emit("posts.addComment", { postID: 5 });

        await broker.test.waitForEvent("posts.updated");

        expect(broker.test.eventEmitted("posts.updated")).toBe(true);
        expect(broker.test.eventEmittedTimes("posts.updated")).toBe(1);
        expect(broker.test.eventEmittedWithParams("posts.updated", { postID: 5 })).toBe(true);

        broker.test.clearEvents(); // clearing saved event calls
    });
});

Mocking action calls

Need a testing middleware which wraps the broker.call method & collect every call. It can register mocked calls, as well. Mock functions

Assertion methods

const { createBroker } = require("moleculer").Testing;

describe("Test", ()=> {
    const broker = createBroker();

    broker.createService({
        name: "posts",

        actions: {
            async get(ctx) {
                const post = this.posts[ctx.params.id];
                post.author = await ctx.call("users.get", { id: post.authorID });
                return post;
            }
        }
    })

    beforeAll(() => broker.start());
    afterAll(() => broker.stop());

    it("should resolve author of post", async () => {
        broker.test.mockAction("users.get").withParams({ id: 5 }).withMeta({}).returnValue({ id: 5, name: "John", email: "john@moleculer.services" });

        const res = await broker.call("posts.get", { id: 2 });

        expect(res).toEqual({
            id: 2,
            title: "Post title",
            authorID: 5,
            author: {
                id: 5, 
                name: "John", 
                email: "john@moleculer.services"
            }
        });

        expect(broker.test.actionCalled("users.get")).toBe(true);
        expect(broker.test.actionCalledTimes("users.get")).toBe(1);
        expect(broker.test.actionCalledWithParams("users.get", { id: 5 })).toBe(true);

        broker.test.clearActions(); // clearing saved action calls
    });
});
IssueHuntBot commented 4 years ago

@icebob has funded $10.00 to this issue.


IssueHuntBot commented 3 years ago

@icebob has funded $40.00 to this issue.


IssueHuntBot commented 2 months ago

@activitypods has funded $50.00 to this issue.