Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I've run into this issue in real code, but I put together a trivial example to prove the point.

The below code works fine. I've set up a variable in my root describe() block that is accessible within my sub-describe()s' it() blocks.

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/

However, if I try to DRY up my code a little by doing the following, it breaks:

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/

Within the nested describe() scope, the orchard object is undefined, even though it's defined within the it() blocks within it.

Is this intentional on the part of Jasmine's developers, possibly to avoid issues with resetting the object in beforeEach() and possible breaking some references? How do they make it happen? I could see how this might be useful, I'm just very curious as to how it works. (My guess is some apply() or call() magic, but I'm not sure how...)

--

As a side-note, I can still DRY up my code by simply using another beforeEach() block:

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();         });     }); }); 
like image 647
Ken Bellows Avatar asked Feb 16 '15 16:02

Ken Bellows


People also ask

Does beforeEach run before each describe?

The beforeEach method is a feature (hook) in test libraries that you can use to set preconditions for each test. Just like the name suggests, it runs before each test in your test suite.

Which function is called before each test specification is run?

The beforeEach function is called once before each spec is run in the describe block, and the afterEach function is called once after each spec is run. Both the functions are very useful for refactoring and optimizing the common code.


2 Answers

The body of a describe block is executed before the beforeEach blocks.

This is exactly as expected. The problem is that your var trees variable is trying to access orchard before it has been initialized. The body of a describe block is executed before the beforeEach blocks. To solve this problem, the third code snippet is the only way to go.

Jasmine will first execute the describe blocks, and then execute the beforeEach blocks before running each test.

like image 95
Andrew Eisenberg Avatar answered Sep 23 '22 05:09

Andrew Eisenberg


Well you could still initialize variables outside the beforeEach block. I generally do it for constants and still remain DRY without introducing beforeEach blocks.

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();         });     }); }); 
like image 40
aeje Avatar answered Sep 20 '22 05:09

aeje