What prevents variables defined in outer blocks from being accessed by nested describe() blocks?

In my real code, I encountered a problem that I wanted to demonstrate with a simple example.

The code below functions properly. I defined a variable in the root describe() block that can be accessed in the it() blocks of nested describe()s.

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, when I attempted to refactor my code to make it more concise, the following approach failed:

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/

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

Was this intentional by Jasmine's developers to prevent issues with resetting the object in beforeEach()? How does it work? It seems like some sort of magic with apply() or call(), but I'm unsure...

--

On a related note, I can still streamline my code by adding 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();
        });
    });
});

Answer №1

The execution order of a describe block is before the beforeEach blocks.

This behavior is completely expected. The issue arises when your var trees variable tries to access orchard without being initialized first. In a nutshell, the code within a describe block runs prior to the beforeEach blocks. To rectify this situation, the solution lies within the third code snippet provided.

Jasmine follows a specific sequence where it handles describe blocks initially followed by beforeEach blocks before proceeding with each test case.

Answer №2

One way to maintain DRY code is by declaring variables outside of the beforeEach block, especially for constants. This approach can help streamline your code without the need for additional beforeEach blocks.

describe('simple object', function () {
    const garden = {
        flowers: {
            rose: 5,
            lily: 8
        },
        shrubs: {
            hydrangea: 12,
            azalea: 15
        }
    };

    describe('flowers', function () {
        const flowers = garden.flowers;

        it('should have roses and lilies', function () {
            expect(flowers.rose).toBeDefined();
            expect(flowers.lily).toBeDefined();

            expect(flowers.rose).toEqual(5);
            expect(flowers.lily).toEqual(8);
        });

        it('should NOT have tulips or daisies', function () {
            var flowers = garden.flowers;

            expect(flowers.tulip).toBeUndefined();
            expect(flowers.daisy).toBeUndefined();
        });
    });
});

Answer №3

Let's analyze the third code snippet and explore how it can be refactored:

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

    beforeEach(function () {
        farm = {
            animals: {
                cows: 5,
                horses : 10
            },
            crops: {
                corn: 100,
                wheat: 75
            }
        };
    });

    describe('animals', function () {

        it ('should have cows and horses', function() {
            expect (farm.animals.cows).toBeDefined();
            expect (farm.animals.horses).toBeDefined();

            expect (farm.animals.cows).toEqual(5);
            expect (farm.animals.horses).toEqual(10);
        });
        it ('should NOT have pigs or chickens', function() {
            expect (farm.animals.pigs).toBeUndefined();
            expect (farm.animals.chickens).toBeUndefined();
        });
    });
});

If you are new to using Mocha, here is a breakdown of the above code:

  1. describe sets up a test suite with a user-defined name, in this case, "simple object".
  2. A test suite can contain nested suites, allowing for more organized testing.
  3. The variable farm remains accessible throughout all functions and suites within the test suite "simple object".
  4. An it block represents a specific test case or specification.
  5. When running tests, Mocha first visits all the declared it blocks.
  6. During execution, Mocha handles the beforeEach function which assigns values to farm.animals.
  7. Therefore, there's no need to rewrite a beforeEach function inside a sub suite. You can simply omit

    beforeEach (function() { animals = farm.animals; });

  8. Compare the updated snippet below with the original third snippet above.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Incorporate MUX Player (Video) into Angular versions 14 or 15

Mux offers a video API service with its own player: MUX Player I am interested in integrating this npm package specifically into a component in Angular 14/15. The JavaScript should only be loaded when this particular component is rendered. Integration Th ...

Multiple occurrences of the cookie found in the document.cookie

My client is using IE9 and the server is asp.net (specifically a SharePoint application page). Within the Page_Load method of a page, I implemented the following code: Response.Cookies["XXXXX"].Value = tabtitles.IndexOf(Request.Params["tab"]).ToString(); ...

Setting up a simple configuration for WebGl and Javascript

I am currently using OSX 10.9.5 (Mavericks) and following a WebGL tutorial script provided on . I copied and pasted the code from the tutorial into TextEdit and saved it as HTML with the following file structure: webgl | +--index.html | ...

"Explore a different approach to file uploading with React Native JavaScript by using either blob or base64 encoding

I am in the process of uploading visuals. When I employ this technique, it functions as expected: formData.append("file",{uri,type,name}); Yet, I prefer not to transmit my visual using the URI. My intention is to divide the visual into distinct sections ...

Tips for ensuring proper function of bullets in glidejs

I am currently working on implementing glidejs as a slider for a website, but I am facing issues with the bullet navigation. The example on glidejs' website shows the bullets at the bottom of the slider (you can view it here: ). On my site, the bullet ...

Eliminate FormData usage from the Next.JS backend application

Looking to replicate the steps outlined in this guide: https://medium.com/@_hanglucas/file-upload-in-next-js-app-router-13-4-6d24f2e3d00f for file uploads using Next.js, encountering an error with the line const formData = await req.formData();. The error ...

Activating a inaccessible element

Attempting to enable a disabled element upon clicking a P element, the following code snippet demonstrates storing a value from one textbox into another. The appended div contains a subsequent textbox which will be disabled. On mouseover of the div, option ...

Troubleshooting Angular 5: Interceptor Fails to Intercept Requests

I have a valid JWT token stored in local storage and an interceptor that I borrowed from a tutorial. However, the interceptor is not intercepting requests and adding headers as expected. Here's where I am making a request: https://github.com/Marred/ ...

How can Javascript be used to interact with buttons and select options?

Can anyone help me with creating a JavaScript code that can select an option from a drop down, click on that option, and then add it to the cart by clicking another button? So far, I have attempted the following: browser.getelementbyid("id").invokemembe ...

Can you explain the meaning of this AJAX code snippet?

I've been researching online for information, but I'm struggling to find any details about the AJAX code snippet that I'm using: function getEmployeeFilterOptions(){ var opts = []; $checkboxes.each(function(){ if(this.checke ...

AngularJS - Unchecked radio button issue

Within my code, there is an ng-repeat including several radio buttons: <div class="panel panel-default" ng-repeat="item in vm.itemList"> ... <td ng-show="item.edit"> <div class="row"> ...

a single line within a compartment

I am encountering an issue with the code snippet below, which is responsible for generating the location of different records within a table: return ( <TableRow> <TableCell > // some code_1 < ...

jQuery AJAX POST Request Fails to SendIt seems that the

The issue I am experiencing seems to be directly related to the jQuery $.ajax({...}); function. In PHP, when I print the array, I receive a Notice: Undefined index. I would greatly appreciate any advice or guidance on this matter. <script> $(docume ...

creating an HTML table from JSON data using a specific key

In this json file, I am looking to create separate tables based on the IDs provided. For example, ID: "50" should be displayed in one table, while ID: "57" should be shown in another table within the same context. { "version": "5.2", "user_type": ...

Multiple card displays not working with Javascript Fetch API

I wrote a script that retrieves data from a URL and then organizes it into tables for display to the user. Initially, the script functioned correctly. However, I decided to enhance its flexibility by introducing two parameters - name and HTML div name wher ...

Establishing specific categories for a universal element

I have been working on creating an input component that functions as a custom select for enums in my application. I have tried defining them for different types using concise one-liners but have run into various typing issues. Here is what I have so far: ...

The Ajax data was not displayed in the console log, instead an error message was returned stating "http://localhost/IFICSV3/public/sla/sla/getbranch/180 not found"

I am attempting to make a dropdown option dependent on another using ajax. I want to view the data in the console to see if it is successful or not. I expect the data to be displayed in the console log, but instead, an error is being given. http://local ...

Display a thumbnail image using a v-for loop

Looking for help with implementing a photo preview in my code using BootstrapVue. The Vue devtools show that the form-file contains the image, but my 'watch' isn't functioning properly. Any assistance would be greatly appreciated! Here is ...

The toggleClass() function is only toggling between a single class

I'm currently facing an issue with a jQuery/Angular function that is triggered on click. <a ng-click="like()" href="#" class="like like--yes"></a> Essentially, my goal is to switch between the classes like--yes and like--no when the like ...

Troubles with proper functionality of antd's DatePicker.RangePicker within a React Function Component

I am currently utilizing React and the antd library, attempting to incorporate the DatePicker.RangePicker into my project. Initially, when I directly embed the RangePicker within the component, everything works smoothly. However, for better code organizat ...