typescript - 如何在 Jest 中的 beforeAll/beforeEach 和测试之间共享数据?

标签 typescript async-await jestjs web-api-testing

我们使用 jest 来测试我们的 API,并且有相当复杂的场景。我们使用 beforeAll为每个测试设置通用辅助变量的函数,有时设置租户分离,在其他情况下,我们使用 beforeEach为测试设置租户分离的功能,为测试租户提供一些默认配置,...

例如,测试可能像这样(如您所见,我们使用 TypeScript 来编写测试,以防万一):

let apiClient: ApiClient;
let tenantId: string;

beforeAll(async () => {
    apiClient = await getClientWithCredentials();
});

beforeEach(async () => {
    tenantId = await createNewTestTenant();
});

describe('describing complex test scenario', () => {
    it('should have some initial state', async () => {
        await checkState(tenantId);
    });

    it('should have some state after performing op1', async () =>{
        await op1(tenantId);
        await checkStateAfterOp1(tenantId);
    });

    it('should have some state after performing op2', async () =>{
        await op2(tenantId);
        await checkStateAfterOp2(tenantId);
    });

    it('should have some state after performing op1 and op2', async () =>{
        await op1(tenantId);
        await op2(tenantId);
        await checkStateAfterOp1AndOp2(tenantId);
    });

    it('the order of op1 and op2 should not matter', async () =>{
        await op2(tenantId);
        await op1(tenantId);
        await checkStateAfterOp1AndOp2(tenantId);
    });    
});

describe('another similar complex scenario', () => {
    // ... you see where this is going
});

问题是,共享由 beforeAll 初始化的变量的最佳方式是什么?和 beforeEach ? - 如果使用 --runInBand 执行上述测试,则​​有效选项,它“......在当前进程中连续运行所有测试......”

但是当并行执行时,它开始非常随机地失败,主要是指tenantId未定义。鉴于这些测试是大约 200 个类似测试的一部分,因此全部通过。同时,它取决于机器。具有 8 核/16 线程的构建代理只有 50-60% 通过测试。我的四核 CPU 同事通过了约 80% 的测试,而对我来说,双核 CPU 有时只有 1-2 次测试失败,其他时候约 10 次。所以很明显,这取决于并行度。

我发现了 2 个 GitHub 问题,人们提到了使用 this 的可能性。共享上下文(不再起作用)或将所有内容封装在 describe 中:
  • https://github.com/facebook/jest/issues/3553
  • https://github.com/facebook/jest/issues/3673

  • 所以我尝试了一个非常幼稚的方法:
    describe('tests', () => {
        let apiClient: ApiClient;
        let tenantId: string;
    
        beforeAll(async () => {
            apiClient = await getClientWithCredentials();
        });
    
        beforeEach(async () => {
            tenantId = await createNewTestTenant();
        });
    
        describe('describing complex test scenario', () => {
            it('should have some initial state', async () => {
                await checkState(tenantId);
            });
    
            it('should have some state after performing op1', async () =>{
                await op1(tenantId);
                await checkStateAfterOp1(tenantId);
            });
    
            it('should have some state after performing op2', async () =>{
                await op2(tenantId);
                await checkStateAfterOp2(tenantId);
            });
    
            it('should have some state after performing op1 and op2', async () =>{
                await op1(tenantId);
                await op2(tenantId);
                await checkStateAfterOp1AndOp2(tenantId);
            });
    
            it('the order of op1 and op2 should not matter', async () =>{
                await op2(tenantId);
                await op1(tenantId);
                await checkStateAfterOp1AndOp2(tenantId);
            });    
        });
    
        describe('another similar complex scenario', () => {
            // ... you see where this is going
        });
    });
    

    但这似乎没有任何效果。
    我真的很想并行运行测试,但我在文档中找不到任何相关内容。也许我不知道我应该寻找什么?

    最佳答案

    这对你有用吗?

    describe('tests', () => {
        let apiClient: ApiClient;
        let tenantIds: {id: string, used: boolean}[];
    
        const findUnusedTenantId = () => {
          const tenant = tenantIds.find(a => !a.used);
          tenant.used = true; 
          return tenant.id
        }
    
        beforeAll(async () => {
            apiClient = await getClientWithCredentials();
        });
    
        beforeEach(async () => {
            const id = await createNewTestTenant();
            tenantIds.push({id, used: false})
        });
    
        describe('describing complex test scenario', () => {
            let tenantId: string
            it('should have some initial state', async () => {
                tenantId = fineUnusedTenantId();
                await checkState(tenantId);
            });
    
            it('should have some state after performing op1', async () =>{
                await op1(tenantId);
                await checkStateAfterOp1(tenantId);
            });
    
            // ...
        });
        describe('next scenario', () => {
            let tenantId: string
            it('first test', async () => {
                tenantId = fineUnusedTenantId();
                await checkState(tenantId);
            });
    
    

    你可能想要一个 afterAll Hook 来清理数据库

    关于typescript - 如何在 Jest 中的 beforeAll/beforeEach 和测试之间共享数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48528502/

    相关文章:

    selenium-webdriver - 当函数没有返回值时,使用页面对象模型返回 promise 或在函数中使用 async/await 是更好的做法

    javascript - 使用 Jest 测试时,类型错误 : AWS. DynamoDB.DocumentClient 不是构造函数

    javascript - 模拟获取 : Jest test says response is wrong, 但实际运行代码按预期工作

    javascript - 更新 chartjs 和 angular 中的图表

    javascript - 如何以 Angular 返回 Observable/http/async 调用的响应?

    javascript - 可能未处理的 promise 拒绝(id : 0) React Native AsyncStorage

    generics - 为什么 Rust 不能将带有生命周期参数的异步函数强制转换为函数指针?

    unit-testing - 在Jest中进行每次测试之前运行全局测试设置

    javascript - 如何使用字符串引用对象数组项?

    angular - 使用 Firebase 身份验证使用户 session 过期