簡體   English   中英

如何在JS中測試工廠方法?

[英]How to test factory method in JS?

背景

我在JS中有一個工廠方法,可以為我的node.js應用程序創建一個對象。 此工廠方法接收一些參數,我想測試是否正確創建了對象。

const LibX = require("libX");

const obj = deps => {

    const { colorLib } = deps;

    const hello = () => {
        console.log(colorLib.sayHello()); // prints a phrase with cool colors
    };

    return {
        hello
    };
};

//Here I return `obj` with all dependencies included. Ready to use!
const objFactory = ({animal, owner = "max"}) => {

    //For example,I need to know if phrase is being well constructed
    const phrase = `${owner} from ${animal} says hello!`;

    const lib = new LibX(phrase);
    return obj({ colorLib: lib });
};

const myObj = objFactory({animal: "cat"});
myObj.hello();

問題

obj函數很容易測試,因為我將所有依賴項傳遞給對象,因此可以存根和監視所有想要的東西。

問題是objFactory ,該函數應該創建一個包含所有內容的obj對象,為了做到這一點,我在那里使用了new LibX ,這意味着我無法模擬它。 我也無法測試該phrase是否構建良好或是否正確傳遞。

這也違反了得墨 the 耳定律,因為我的工廠需要知道一些本不應該知道的東西。

如果不傳遞LibX作為參數(這意味着我將需要一個Factory作為工廠....造成混淆了嗎?),我不知道如何解決此問題。

如何使objFactory易於測試?

您需要問自己的第一個問題是您要測試什么。

您是否需要確保正確構建phrase常量? 如果是這樣,則需要將其提取到單獨的函數並分別進行測試。

也許您想要的是測試myObj.hello();的效果myObj.hello(); 在這種情況下,我建議使hello()返回一個字符串,而不是將其記錄到控制台。 這將使最終效果易於測試。

編寫清晰的代碼將避免產生不可模仿的依賴關系。 不能嘲笑您編寫示例的方法libx (它是一個外部依賴項)。 或者我應該說,它不應該被嘲笑。 從技術上講,也可以對其進行模擬,但是我建議您不要這樣做,因為它會給圖片帶來其自身的復雜性。

1.確保詞組正確構建

這很簡單。 您的單元測試應該看起來像這樣:

it("should build the phrase correctly using all params", () => {
    // given
    const input = {animal: "dog", owner: "joe"};

    // when
    const result = buildPhrase(input);

    // then
    expect(result).to.equal("joe from dog says hello!");
});

it("should build the phrase correctly using only required params", () => {
    // given
    const input = {animal: "cat"};

    // when
    const result = buildPhrase(input);

    // then
    expect(result).to.equal("max from cat says hello!");
});

通過上面的單元測試,您的生產代碼將需要看起來像這樣:

const buildPhrase = function(input) {
    const owner = input.owner || "max";
    const animal = input.animal;

    return `${owner} from ${animal} says hello!`;
};

在這里,短語構建已經過測試。 然后,您可以使用buildPhrase你里面objFactory

2.測試返回方法的效果

這也很簡單。 您為工廠提供輸入,並期望輸出。 輸出將始終是輸入的函數,即相同的輸入將始終產生相同的輸出。 那么,如果可以預測預期的結果,為什么還要測試幕后情況呢?

it("should produce a function that returns correct greeting", () => {
    // given
    const input = {animal: "cat"};
    const obj = objFactory(input);

    // when
    const result = obj.hello();

    // then
    expect(result).to.equal("max from cat says hello!");
});

最終可能會導致您進入以下生產代碼:

const LibX = require("libX");

const obj = deps => {
    const { colorLib } = deps;
    const hello = () => {
        return colorLib.sayHello(); // note the change here
    };

    return {hello};
};

const objFactory = ({animal, owner = "max"}) => {
    const phrase = `${owner} from ${animal} says hello!`;
    const lib = new LibX(phrase);

    return obj({ colorLib: lib });
};

3.模擬require("libx")的輸出

還是不。 如前所述,您實際上不應該這樣做。 不過,如果您被迫這樣做(我將推理留給了這個決定),則可以使用模擬需求或類似的工具。

const mock = require("mock-require");

let currentPhrase;
mock("libx", function(phrase) {
    currentPhrase = phrase;
    this.sayHello = function() {};
});

const objFactory = require("./objFactory");

describe("objFactory", () => {
    it("should pass correct phrase to libx", () => {
        // given
        const input = {animal: "cat"};

        // when
        objFactory(input);

        // then
        expect(currentPhrase).to.be("max from cat says hello!");
    });
});

但是請記住,這種方法比看起來復雜。 模擬require依賴項覆蓋了require的緩存,因此您必須記住清除它,以防萬一有其他不希望依賴項被嘲笑而是依賴於它執行操作的測試。 另外,您必須始終保持警惕,並確保代碼的執行順序(並不總是那么明顯)是正確的。 您必須先模擬依賴項,然后使用require() ,但是要確保這一點並不總是那么容易。

4.注入依賴

模擬依賴項的最簡單方法是始終注入它。 由於在代碼中使用了new ,因此將其包裝在一個可以隨時模擬的簡單函數中可能是有意義的:

const makeLibx = (phrase) => {
    return new LibX(phrase);
};

如果您隨后將其注入到工廠中,則嘲笑將變得微不足道:

it("should pass correct input to libx", () => {
    // given
    let phrase;
    const mockMakeLibx = function(_phrase) {
        phrase = _phrase;
        return {sayHello() {}};
    };
    const input = {animal: "cat"};

    // when
    objFactory(mockMakeLibx, input);

    // then
    expect(phrase).to.equal("max from cat says hello!");
});

顯然,這將導致您編寫如下內容:

const objFactory = (makeLibx, {animal, owner = "max"}) => {
    const phrase = `${owner} from ${animal} says hello!`;
    const lib = makeLibx(phrase);

    return obj({ colorLib: lib });
};

我的最后一條建議是:始終提前計划代碼,並盡可能使用TDD。 如果您編寫生產代碼然后考慮如何進行測試,就會發現自己一遍又一遍地問同樣的問題:我該如何測試它? 我該如何模擬這種依賴性? 這不違反得墨meter耳定律嗎?

雖然您應該問自己的問題是:我希望這段代碼做什么? 我要如何表現? 它的效果應該是什么樣的?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM