# Mock Assimilation

# Problem

You want to ensure internal methods of an object get called as expected and have no other side-effects:

my-class.ts

class MyClass {
    private someProp = { hip: "hop" };

    public run(param1: boolean, param2: string) {
        if (!this.runA(this.someProp, param2))
            return false;
        const resultB = this.runB(this.someProp, param1);
        return this.runC(resultB, this.someProp) === "done";
    }
    private runA(config: any, param: boolean) {
        return !param;
    }
    private runB(config: any, value: string) {
        return value === "foo" ? "bar" : "nope";
    }
    private runC(value: string, config: any) {
        return value === "bar" ? "done" : "huh?";
    }
}

# Solution

You can use mockAssimilate(instance, name, { mock: [], whitelist?: []} to assimilate your instance by overriding methods with mockable functions.

  • You can use whitelist to ensure no property gets touched without you knowing.
  • The names listed in mock are automatically whitelisted.
  • If whitelist is not specified, other properties may be accessed.

Important: Use mockAssimilate only from within it(), test() or beforeEach() blocks.

my-class.spec.ts

import { mockAssimilate } from "mockzilla";

describe("MyClass", () => {
    describe("#run()", () => {
        it("should return false with param1=false and param2='foo'", () => {
            const myInstance = new MyClass();
            const mock = mockAssimilate(myInstance, "myInstance", {
                mock: ["runA"],
                whitelist: ["run", "someProp"],
            });
            // Notice how you get auto-completion and type-checking:
            mock.runA.expect(expect.anything(), true).andReturn(true);

            expect(myInstance.run(false, "foo")).toBe(true);
        });

        it("should return true with param1=true and param2='foo'", () => {
            const myInstance = new MyClass();
            const mock = mockAssimilate(myInstance, "myInstance", {
                mock: ["runA", "runB", "runC"],
                whitelist: ["run", "someProp"],
            });
            // Notice how you get auto-completion and type-checking:
            mock.runA.expect(expect.anything(), true).andReturn(true);
            mock.runB.expect(expect.anything(), "foo").andReturn("bar");
            mock.runC.expect("bar", expect.anything()).andReturn("done");

            expect(myInstance.run(true, "foo")).toBe(true);
            // if the test passes, we know, that `runA/B/C` have been called
            // and that nothing other than `run` and `someProp` have been accessed (get, set or called) during the test.
        });
    });
});

If you want to know more about what you can do with these expect calls, check out Expectations.