简体   繁体   English

如何嵌套 Jest 测试

[英]How to nest Jest tests

I have a situation where I need to create a test that calls a function and checks its return value.我有一种情况,我需要创建一个调用 function 并检查其返回值的测试。 It returns an object so I have to iterate through it and check 100 or so values for correctness.它返回一个 object 所以我必须遍历它并检查 100 个左右的值是否正确。 If one of them fails I want to know which one.如果其中一个失败了,我想知道是哪一个。

I cannot work out how to do this with vanilla Jest such that the test is self-contained and I get a meaningful error message on a failure.我无法弄清楚如何使用 vanilla Jest 执行此操作,以便测试是独立的,并且我会在失败时收到有意义的错误消息。

For example, I can do this: (pseudocode to illustrate, not actual code)例如,我可以这样做:(伪代码来说明,不是实际代码)

describe('Test for function A', () => {
    beforeAll('Create class instance', () => {
        this.inst = new MyClass();
    });

    test('Call function with no parameters', () => {
        const value = this.inst.run();

        for (each key=value) {
            expect(value).toBe(correct); // on failure, doesn't tell me the key, only the value
        }
    });
});

The problem with this is that if value is not correct then the error message is not very helpful, as it doesn't tell me which of the 100 values has the problem.这样做的问题是,如果valuecorrect ,则错误消息不是很有帮助,因为它没有告诉我 100 个值中的哪个有问题。

I can't change to test.each() because then I get an error saying I have nested test() calls which is not allowed.我无法更改为test.each()因为我收到一个错误消息,说我有嵌套的test()调用,这是不允许的。

If I use an inner test() and change the parent test() to describe() then the code becomes this:如果我使用内部test()并将父test()更改为describe()则代码变为:

describe('Test for function A', () => {
    beforeAll('Create class instance', () => {
        this.inst = new MyClass();
    });

    describe('Call function with no parameters', () => {
        const value = this.inst.run();

        for (each value) {
            test(`Checking ${value.name}`, () => {
                expect(value).toBe(correct);
            });
        }
    });
});

This would give me a detailed error message except this.inst.run() is called during test set up, before this.inst has been set by beforeAll() , so it fails.这会给我一个详细的错误消息,除了this.inst.run()在测试设置期间被调用,在this.instbeforeAll()设置之前,所以它失败了。 (Jest runs all the describe() blocks first, then beforeAll() , then test() . This means I call this.inst.run() first in a describe() block before the beforeAll() block creates the class instance.) (Jest 首先运行所有describe()块,然后beforeAll()test() 。这意味着我首先在describe()块中调用this.inst.run() ,然后beforeAll()块创建 class 实例。 )

Is there any way that this is possible to achieve?有什么方法可以实现吗? To have a test that requires an object created and shared amongst all the child tests, a test group that calls a function to get data for that group, then a bunch of tests within the group?要进行需要在所有子测试之间创建和共享 object 的测试,调用 function 的测试组来获取该组的数据,然后在组内进行一堆测试?

Yes, it is possible according to the order of execution of describe and test blocks:是的,可以根据describetest块的执行顺序:

describe("Test for function A", () => {
  this.inst = new MyClass();
  afterAll("Create class instance", () => { //--> use this instead of beforeAll
    this.inst = new MyClass();
  });

  test("Should be defined", () => {
    //--> at least one test inside describe
    expect(inst).toBeTruthy();
  });

  describe("Call function with no parameters", () => {
    const value = this.inst.run();

    test("Should be defined", () => {
      //--> at least one test inside describe
      expect(value).toBeTruthy();
    });

    for (/*...each value */) {
      test(`Checking ${value.name}`, () => {
        expect(value).toBe(correct);
      });
    }
  });
});

I came up with a workaround for this.我想出了一个解决方法。 It's a bit hacky but it seems to work.这有点hacky,但它似乎工作。 Essentially you use promises to wrap the value you're interested in, so one test will sit there await -ing the result from another test.本质上,您使用承诺来包装您感兴趣的值,因此一个测试将坐在那里await另一个测试的结果。

Obviously this will only work if the tests are run in parallel, or if the sequential ordering is such that the promise is resolved before it is awaited.显然,这仅适用于并行运行测试,或者如果顺序排序使得 promise 在等待之前解决。

The only trick below is that the await is placed in a beforeAll() block, so that the value is available to all tests within that describe() section.下面唯一的技巧是将await放置在beforeAll()块中,以便该值可用于该describe()部分中的所有测试。 This avoids the need to await in each individual test.这避免了在每个单独的测试中await的需要。

The benefit of this is that the test set up (creating the object) is within a test() so exceptions are captured, and the checks themselves ( expect().toBe() ) are in separate tests so that the test name can be set to something descriptive.这样做的好处是测试设置(创建对象)在test()中,因此会捕获异常,并且检查本身( expect().toBe() )在单独的测试中,因此测试名称可以是设置为描述性的东西。 Otherwise if your expect() calls are in a for loop, when one fails there's no way to figure out which array entry was at fault.否则,如果您的expect()调用在 for 循环中,则当一个失败时,无法确定哪个数组条目有问题。

It's a lot of work just because you can't supply a description on the expect() call (unlike other testing frameworks), but if you're stuck with Jest then this does at least work.仅仅因为您无法在expect()调用上提供描述(与其他测试框架不同),这是很多工作,但如果您被 Jest 困住,那么这至少可以工作。 Hopefully one day they will add a per-expect description to avoid all this.希望有一天他们会添加一个符合预期的描述来避免这一切。

Here is some sample pseudocode:这是一些示例伪代码:

describe('Test for function A', () => {
    let resolveValue;
    let promiseValue = new Promise(resolve => resolveValue = resolve);

    describe('Create class instance', () => {
        test('Run processing', () => {
            this.inst = new MyClass();
            // inst.run() is now called inside a test(), so any failures will be caught.
            const value = this.inst.run();
            resolveValue(value); // release 'await promiseValue' below
        });
    });

    describe('Call function with no parameters', () => {
        let value; // this is global within this describe() so all tests can see it.

        beforeAll(async () => {
            // Wait for the above test to run and populate 'value'.
            value = await promiseValue;
        });

        for (each value) {
            // Check each value inside test() to get a meaningful test name/error message.
            test(`Checking ${value.name}`, () => {
                // 'value' is always valid as test() only runs after beforeAll().
                expect(value).toBe(correct);
            });
        }
    });
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM