首页 > 解决方案 > 如果我要对 if 子句中发生的事情进行单元测试,我应该将所有内容模拟到 if 子句中吗?

问题描述

场景

我正在为类似于下面的模块编写单元测试

服务.js

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

let managedData = {};

function manageData(dataId) {
  let users = client.retrieveUsers(); //Will return an array of strings. For example: a list of user names
  let user = client.retrieveCurrentUser(); //Will return a string. For example: one user name
  let userIsPresentInList = users.includes(user); //Check if the user is present in a broader user list
  if(userIsPresentInList) {
    managedData[dataId] = client.retrieveDataWithId(dataId);
  }
}


function unmanageData(dataId) {
  client.doSomethingToTheDataWithId(dataId)
  delete managedData[dataId]
}

module.exports = { 
  manageData,
  unmanageData
}

我的一些单元测试的目标是当用户出现在用户列表中时会发生什么,即 if 子句内部会发生什么

问题是:如果我对 if 子句进行单元测试,我是否应该模拟该子句的所有内容(下面的选项 1)?或者我应该创建一种以 if 子句结尾的“直接”方式(下面的选项 2)?

考试

选项 1) 通过 describe 块对测试进行分组,并在 beforeEach/beforeAll: 中进行模拟:涉及 if 子句内部内容的场景被分组在一个 describe 块中,并且在 if 子句之前对所有语句进行模拟

选项 2) 模拟模块内部的中间函数: service.js 被重构。检查用户是否在用户列表中的代码被提取到模块内的一个新函数中。然后模拟这个函数。这样,if 子句之前的任何内容都无关紧要,测试只关注 if 子句中的内容是否有效

service.test.js

const service = require("./service");
const client = require("./../client");
jest.mock("./../client");

beforeEach(() => {
  jest.clearAllMocks();
});

describe('Sucesful management', () => {


    //OPTION 1
    beforeEach(() => {
        client.retrieveUsers = jest.fn(() => ["a", "b", "c"]);
        client.retrieveCurrentUser = jest.fn(() => "a");
    })

    //OPTION 2
    beforeEach(() => {
        service.isUserPresentInUsersList = jest.fn(() => true);
    })

    afterEach(() => {
      service.isUserPresentInUsersList.mockRestore();
    })

    it("Given the user belongs to the user list, should retrieve the data calleing manageData", async () => {
        //Act
        service.manageData("D");
        //Assert
        expect(client.retrieveDataWithId).toHaveBeenCalledWith("D");
      });
    
      it("Given the user belongs to the user list, should do something with the data when calling unmanageData", async () => {
        //Act
        service.manageData("D");
        service.unmanageData("D");
        //Assert
        expect(client.doSomethingToTheDataWithId).toHaveBeenCalledWith("D");
      });
})

在这种情况下有更好的选择吗?这些方法中的任何一个都可怕吗?

一个扩展的问题是:哪些开源项目具有良好的测试实践,我可以从中获得灵感?

标签: node.jsunit-testingjestjsmocking

解决方案


推荐阅读