为什么嵌套的 describe() 块看不到外部块中定义的变量?

Why can't nested describe() blocks see vars defined in outer blocks?

我已经 运行 在实际代码中解决了这个问题,但我举了一个简单的例子来证明这一点。

下面的代码工作正常。我在我的根 describe() 块中设置了一个变量,可以在我的子 describe()it() 块中访问。

describe('simple object', function () {
    var orchard;

    beforeEach(function () {
        orchard = {
            trees: {
                apple: 10,
                orange : 20
            },
            bushes: {
                boysenberry : 40,
                blueberry: 35
            }
        };
    });

    describe('trees', function () {
        it ('should have apples and oranges', function() {
            var trees = orchard.trees;

            expect (trees.apple).toBeDefined();
            expect (trees.orange).toBeDefined();

            expect (trees.apple).toEqual(10);
            expect (trees.orange).toEqual(20);
        });
        it ('should NOT have pears or cherries', function() {
            var trees = orchard.trees;

            expect (trees.pear).toBeUndefined();
            expect (trees.cherry).toBeUndefined();
        });
    });
});

http://jsfiddle.net/w5bzrkh9/

但是,如果我尝试通过执行以下操作稍微干燥我的代码,它就会中断:

describe('simple object', function () {
    var orchard;

    beforeEach(function () {
        orchard = {
            trees: {
                apple: 10,
                orange : 20
            },
            bushes: {
                boysenberry : 40,
                blueberry: 35
            }
        };
    });

    describe('trees', function () {
        var trees = orchard.trees; // TypeError: Cannot read property 'trees' of undefined

        it ('should have apples and oranges', function() {
            expect (trees.apple).toBeDefined();
            expect (trees.orange).toBeDefined();

            expect (trees.apple).toEqual(10);
            expect (trees.orange).toEqual(20);
        });
        it ('should NOT have pears or cherries', function() {
            expect (trees.pear).toBeUndefined();
            expect (trees.cherry).toBeUndefined();
        });
    });
});

http://jsfiddle.net/goqcev42/

在嵌套的 describe() 范围内,orchard 对象未定义,即使它是在其中的 it() 块中定义的。

Jasmine 的开发人员是否有意这样做,可能是为了避免在 beforeEach() 中重置对象并可能破坏某些引用的问题?他们如何做到这一点?我可以看到这可能有什么用,我只是很好奇它是如何工作的。 (我的猜测是一些 apply()call() 魔法,但我不确定如何...)

--

作为旁注,我仍然可以通过简单地使用另一个 beforeEach() 块来干燥我的代码:

describe('simple object', function () {
    var orchard;

    beforeEach(function () {
        orchard = {
            trees: {
                apple: 10,
                orange : 20
            },
            bushes: {
                boysenberry : 40,
                blueberry: 35
            }
        };
    });

    describe('trees', function () {
        var trees;

        beforeEach(function() {
            trees = orchard.trees;
        });

        it ('should have apples and oranges', function() {
            expect (trees.apple).toBeDefined();
            expect (trees.orange).toBeDefined();

            expect (trees.apple).toEqual(10);
            expect (trees.orange).toEqual(20);
        });
        it ('should NOT have pears or cherries', function() {
            expect (trees.pear).toBeUndefined();
            expect (trees.cherry).toBeUndefined();
        });
    });
});

describe 块的主体在 beforeEach 块之前执行。

这完全符合预期。问题是您的 var trees 变量在初始化之前试图访问 orcharddescribe 块的主体在 beforeEach 块之前执行。 要解决此问题,第三个代码片段是唯一的方法。

Jasmine 将首先执行 describe 块,然后在 运行 每次测试之前执行 beforeEach 块。

好吧,您仍然可以在 beforeEach 块之外初始化变量。我通常对常量执行此操作,并且在不引入 beforeEach 块的情况下仍然保持 DRY。

describe('simple object', function () {
    const orchard = {
        trees: {
            apple: 10,
            orange: 20
        },
        bushes: {
            boysenberry: 40,
            blueberry: 35
        }
    };


    describe('trees', function () {
        const trees = orchard.trees;

        it('should have apples and oranges', function () {


            expect(trees.apple).toBeDefined();
            expect(trees.orange).toBeDefined();

            expect(trees.apple).toEqual(10);
            expect(trees.orange).toEqual(20);
        });
        it('should NOT have pears or cherries', function () {
            var trees = orchard.trees;

            expect(trees.pear).toBeUndefined();
            expect(trees.cherry).toBeUndefined();
        });
    });
});

让我们来看第三个代码片段。此外,它可以重构如下:

describe('simple object', function () {
    var orchard;

    beforeEach(function () {
        orchard = {
            trees: {
                apple: 10,
                orange : 20
            },
            bushes: {
                boysenberry : 40,
                blueberry: 35
            }
        };
    });

    describe('trees', function () {

        it ('should have apples and oranges', function() {
            expect (orchard.trees.apple).toBeDefined();
            expect (orchard.trees.orange).toBeDefined();

            expect (orchard.trees.apple).toEqual(10);
            expect (orchard.trees.orange).toEqual(20);
        });
        it ('should NOT have pears or cherries', function() {
            expect (orchard.trees.pear).toBeUndefined();
            expect (orchard.trees.cherry).toBeUndefined();
        });
    });
});

对于 Jasmine 的新手,这就是你如何解释上面的代码:\

  1. describe定义了一个test suite。这里的 test suite 名称是用户定义的简单字符串,例如 "simple object".
  2. A test suite 本身可以包含其他 test suites,这意味着 describe 可以包含嵌套套件。
  3. 就像其他编程语言一样,orchid 对于 simple object 测试套件中定义的所有函数和套件是全局的。
  4. It 块称为 specificationSPECIt 个块包含单独的测试。
  5. Jasmine执行测试用例时,它将首先访问it块,这意味着它将遍历所有it块声明。
  6. Jasmine 实际上执行测试用例时,它将检查beforeEach函数,因此orchard得到trees值分配给它。
  7. 因此你不需要在sub suite里面写一个beforeEach函数。你可以简单地忽略

    beforeEach (function() { trees = orchard.trees; });

  8. 现在将下面的最新代码段与上面的第三个代码段进行比较。