Jest mock只在文件的顶部工作,而不在“describe”或“it”块中工作

lb3vh1jj  于 2023-11-15  发布在  Jest
关注(0)|答案(1)|浏览(142)

我试图在一个没有单元测试的现有项目中引入一个小的更改,并决定尝试学习足够的nodejs和jest知识,以便在更改中包含测试。然而,我无法让mock像我期望的那样在python中工作。该项目使用godaddy的“kubernetes-client”库,并尝试从envvar“KUBECONFIG”创建一个config对象,如下所示:

// a few lines into server.js
// Instantiate Kubernetes client
const Client = require('kubernetes-client').Client
const config = require('kubernetes-client').config;
if (process.env.KUBECONFIG) {
    client = new Client({
        config: config.fromKubeconfig(config.loadKubeconfig(process.env.KUBECONFIG)),
        version: '1.13'
    });
}
else {
    client = new Client({ config: config.getInCluster(), version: '1.9' });
}

字符串
在我的测试环境中,我不希望任何API调用,所以我尝试模拟它:

// __tests__/server.test.js
// Set up mocks at the top because of how server.js sets up k8s client
const k8sClient = require('kubernetes-client');
const narp = 'narp';
jest.mock('kubernetes-client', () => {
    const noConfigmaps = jest.fn(() => {
        throw narp;
    });
    const namespaces = jest.fn().mockReturnValue({
        configmaps: jest.fn().mockReturnValue({
            get: noConfigmaps
        })
    });
    const addCustomResourceDefinition = jest.fn().mockReturnThis()
    const mockClient = {
        api: {
            v1: {
                namespaces
            }
        },
        addCustomResourceDefinition: jest.fn().mockReturnThis(),
    };
    return {
        Client: jest.fn(() => mockClient),
        config: {
            fromKubeconfig: jest.fn().mockReturnThis(),
            loadKubeconfig: jest.fn().mockReturnThis(),
            getInCluster: jest.fn().mockReturnThis()
        },
    };
});
const app = require('../server.js')
const supertest = require('supertest');
const requestWithSuperTest = supertest(app.app);
describe('Testing server.js', () => {
    afterAll(() => {
        app.server.close();
    });
    describe('Tests with k8s client throwing error when fetching configmaps', () => {
        it("finds a resource's ingressGroup by name", () => {
            var resource = {
                "spec": {
                    "ingressClass": "foo",
                    "ingressTargetDNSName": "foo"
                }
            };
            var ingressGroups = [
                {
                    "ingressClass": "bar",
                    "hostName": "bar",
                    "name": "barName"
                },
                {
                    "ingressClass": "foo",
                    "hostName": "foo",
                    "name": "fooName"
                }
            ];
            expect(app.findMatchingIngressGroupForResource(resource, ingressGroups)).toBe("fooName");
        });

        it('GET /healthcheck should respond "Healthy"', async () => {
            const resp = await requestWithSuperTest.get('/healthcheck');
            console.log("Response in Testing Endpoints: " + JSON.stringify(resp));
            expect(resp.status).toEqual(200);
            expect(resp.type).toEqual(expect.stringContaining('text'));
            expect(resp.text).toEqual('Healthy');
        });
        it('Tests getIngressGroups() rejects with error when it cannot get configmaps', async () => {
            app.getIngressGroups()
                .then()
                .catch(error => {
                    expect(error).toEqual("Failed to fetch Ingress Groups: " + narp);
                });
        });
    });
});


通过这种设置,测试通过了(尽管我怀疑这是没有意义的)。如果我尝试使用beforeEach函数(或不使用)将mock移动到describeit块中,以便我可以更改行为以返回mock数据而不是抛出错误,我会立即收到k8s客户端的错误,抱怨它找不到我的kubeconfig/clusterconfig:

$ npm run testj

> testj
> jest --detectOpenHandles

kubernetes-client deprecated require('kubernetes-client').config, use require('kubernetes-client/backends/request').config. server.js:45:44
kubernetes-client deprecated loadKubeconfig see https://github.com/godaddy/kubernetes-client/blob/master/merging-with-kubernetes.md#request-kubeconfig- server.js:49:42
 FAIL  __tests__/server.test.js
  ● Test suite failed to run

    ENOENT: no such file or directory, open 'NOT_A_FILE'

      44 | if (process.env.KUBECONFIG) {
      45 |     client = new Client({
    > 46 |         config: config.fromKubeconfig(config.loadKubeconfig(process.env.KUBECONFIG)),
         |                                              ^
      47 |         version: '1.13'
      48 |     });
      49 | }

      at node_modules/kubernetes-client/backends/request/config.js:335:37
          at Array.map (<anonymous>)
      at Object.loadKubeconfig (node_modules/kubernetes-client/backends/request/config.js:334:28)
      at Object.eval [as loadKubeconfig] (eval at wrapfunction (node_modules/kubernetes-client/node_modules/depd/index.js:425:22), <anonymous>:5:11)
      at Object.<anonymous> (server.js:46:46)


如果任何人遇到这种行为之前或看到一些明显错误的线路,我真的很感激任何提示或信息。谢谢!

sqxo8psd

sqxo8psd1#

我不得不改变一些东西来让它工作:

  • jest.doMock()而不是jest.mock()
  • 在模块范围内使用describe块内的let app而不是const app
  • beforeEach()调用jest.resetModules()
  • 一个afterEach(),它调用app.close()
  • 在覆盖mock的it块中,在覆盖之前显式调用jest.resetModules()
  • 在覆盖mock的it块中,调用app.close()并在调用实际的function-under-test/expect之前重新初始化app

生成的测试文件:

// Set up mocks at the top because of how server.js sets up k8s client
const k8sClient = require('kubernetes-client');
const supertest = require('supertest');
const narp = 'narp';

describe('Testing server.js', () => {
    let app;
    let requestWithSuperTest;
    beforeEach(() => {
        jest.resetModules();
        jest.doMock('kubernetes-client', () => {
            const noConfigmaps = jest.fn(() => {
                throw narp;
            });
            const namespaces = jest.fn().mockReturnValue({
                configmaps: jest.fn().mockReturnValue({
                    get: noConfigmaps
                })
            });
            const addCustomResourceDefinition = jest.fn().mockReturnThis()
            const mockClient = {
                api: {
                    v1: {
                        namespaces
                    }
                },
                addCustomResourceDefinition: jest.fn().mockReturnThis(),
            };
            return {
                Client: jest.fn(() => mockClient),
                config: {
                    fromKubeconfig: jest.fn().mockReturnThis(),
                    loadKubeconfig: jest.fn().mockReturnThis(),
                    getInCluster: jest.fn().mockReturnThis()
                },
            };
        });
        app = require('../server.js');
        requestWithSuperTest = supertest(app.app);
    });
    afterEach(() => {
        app.server.close();
    });

    it("finds a Resource's ingressGroup by name", () => {
        var resource = {
            "spec": {
                "ingressClass": "foo",
                "ingressTargetDNSName": "foo"
            }
        };
        var ingressGroups = [
            {
                "ingressClass": "bar",
                "hostName": "bar",
                "name": "barName"
            },
            {
                "ingressClass": "foo",
                "hostName": "foo",
                "name": "fooName"
            }
        ];
        expect(app.findMatchingIngressGroupForResource(resource, ingressGroups)).toBe("fooName");
    });

    it('GET /healthcheck should respond "Healthy"', async () => {
        const resp = await requestWithSuperTest.get('/healthcheck');
        console.log("Response in Testing Endpoints: " + JSON.stringify(resp));
        expect(resp.status).toEqual(200);
        expect(resp.type).toEqual(expect.stringContaining('text'));
        expect(resp.text).toEqual('Healthy');
    });
    it('Tests getIngressGroups() rejects with error when it cannot get configmaps', async () => {
        expect.assertions(1);
        await app.getIngressGroups()
            .catch(error => {
                expect(error).toEqual("Failed to fetch Ingress Groups: " + narp);
            });
    });
    it('Tests getIngressGroups() succeeds when it gets configmaps', async () => {
        expect.assertions(1);
        jest.resetModules();
        jest.doMock('kubernetes-client', () => {
            const noConfigmaps = jest.fn(() => {
                console.log('Attempted to get mocked configmaps');
                return Promise.resolve({
                    body: {
                        items: []
                    }
                });
            });
            const namespaces = jest.fn().mockReturnValue({
                configmaps: jest.fn().mockReturnValue({
                    get: noConfigmaps
                })
            });
            const addCustomResourceDefinition = jest.fn().mockReturnThis()
            const mockClient = {
                api: {
                    v1: {
                        namespaces
                    }
                },
                addCustomResourceDefinition: jest.fn().mockReturnThis(),
            };
            return {
                Client: jest.fn(() => mockClient),
                config: {
                    fromKubeconfig: jest.fn().mockReturnThis(),
                    loadKubeconfig: jest.fn().mockReturnThis(),
                    getInCluster: jest.fn().mockReturnThis()
                },
            };
        });
        app.server.close();
        app = require('../server.js');
        await app.getIngressGroups()
            .then(result => {
                expect(result).toEqual([])
            });
    });
});

字符串

相关问题