Latest Ext JS 7.8 is now available. Learn more

How to Write Unit Tests with Sencha Test

April 22, 2024 1230 Views

How to Write Unit Tests with Sencha Test

In modern software development, unit testing has become an essential practice to ensure the quality and maintainability of code. Unit tests are low-level tests that focus on individual units or components of an application, verifying their behavior in isolation. By writing and running unit tests, developers can catch bugs early, facilitate refactoring, and improve the overall quality of their codebase.

For developers working with Ext JS framework, Sencha Test provides a powerful and comprehensive solution for writing unit tests. Sencha Test is a unit testing framework designed specifically for Ext JS and Sencha Touch applications, allowing developers to write and run tests for their components, models, stores, controllers, and other parts of their application.

This article will provide an overview of how to get started with unit testing your Ext JS. We’ll cover setting up the framework, writing different types of tests, running test suites, and integrating tests into development workflows.

Definition of Unit Testing

Unit testing is a software testing technique where individual units or components of an application are tested in isolation. A unit can be a function, a method, or a class. The goal of unit testing is to validate that each unit of the software works as expected, ensuring that it produces the correct output for a given input.

Importance of Unit Testing in Software Development

Unit testing offers several benefits that make it an essential practice in modern software development:

  • Early Bug Detection: By writing and running unit tests, developers can catch bugs early in the development cycle, before they propagate and become more costly to fix.
  • Code Quality: Unit tests serve as a safety net, ensuring that code changes or refactoring don’t introduce new bugs or break existing functionality.
  • Refactoring Support: Well-written unit tests make it easier to refactor code, as developers can quickly verify that the refactored code still works as expected.
  • Documentation: Unit tests can serve as living documentation, providing examples of how to use and interact with different components of the codebase.
  • Confidence: With a comprehensive suite of unit tests, developers can have more confidence in their code, making it easier to maintain and extend over time.

Overview of the Sencha Test

Sencha Test is a unit testing framework specifically designed for Ext JS applications. It provides a set of tools and utilities that make it easy to write, run, and maintain unit tests for all parts of an application, including components, models, stores, controllers, and more.

Sencha Test is built on top of Jasmine, a popular behavior-driven development (BDD) testing framework, and extends it with additional features and utilities tailored for Ext JS and Sencha Touch applications.

Setting up the Sencha Test

Before you can start writing unit tests with the Sencha Test, you need to set up the framework and integrate it with your Ext JS project.

Installing Sencha Test

Sencha Test is installed via npm and requires Node.js. After npm install sencha-architect, sencha-test will be available in the node_modules folder.

Configuring Sencha Test

Create a sencha.cfg.xml configuration file that points to the application being tested. Specify things like app folder, test folders, browser used for running tests.

Creating a Test File

To create a new test file, navigate to the path/to/your/tests directory and create a new JavaScript file with a .js extension. For example, MyComponentTest.js.

In this file, you can start writing your unit tests using the Sencha Test framework.

Writing Unit Tests

The structure and best practices for writing unit tests closely follows patterns used in other testing frameworks. Here is an overview:

Describe Blocks

Describe blocks group related tests under a name. This is helpful for organization and filtering tests to run individually.

It Blocks

Individual test cases defined within it blocks. Each it should test a single condition or scenario.

Structure of a Unit Test

A basic unit test structure follows the AAA pattern:

describe "ComponentClassName" {
    beforeEach {
        // initialization logic
    }
    it "should do something" {
        // Arrange
        // Act
        // Assert
    }
}

The Arrange, Act, Assert pattern clearly defines test steps:

  • Arrange – Set up any required preconditions or test data
  • Act – Call the code being tested
  • Assert – Validate results or outcomes match expectations

This structure promotes readable, maintainable tests that are easy to understand at a glance.
Now let’s dive deeper into testing specific areas of an application.

Testing Components

One of the primary use cases for Sencha Test is testing Ext JS components. Sencha Test provides utilities for rendering components, simulating user interactions, and verifying component behavior.

Rendering Components

Before you can test a component, you need to render it. Sencha Test provides a Ext.create helper function that simplifies the process of creating and rendering components.

describe('My Component', function() {
    it('should render correctly', function() {
        // Create and render the component
        var component = Ext.create('MyApp.view.MyComponent', {
            renderTo: Ext.getBody()
        });
        // Verify component rendering
        expect(component.isVisible()).toBe(true);
    });
});

In this example, the Ext.create function is used to create an instance of the MyApp.view.MyComponent component and render it to the document body. The expect function is then used to assert that the component is visible, verifying that it rendered correctly.

Testing Component Methods

You can also test the methods of a component by calling them directly and verifying their behavior.

describe('My Component', function() {
    it('should perform an action correctly', function() {
        // Create and render the component
        var component = Ext.create('MyApp.view.MyComponent', {
            renderTo: Ext.getBody()
        });
        // Call a method and verify the result
        component.doSomething();
        expect(component.getSomeValue()).toBe(expectedValue);
    });
});

In this example, the doSomething method is called on the component instance, and the getSomeValue method is used to retrieve the result. The expect function is then used to assert that the result matches the expected value.

Testing Component Events

Sencha Test also provides utilities for testing component events. You can simulate user interactions, such as clicking a button or typing in a text field, and verify that the appropriate events are fired and handled correctly.

describe('My Component', function() {
    it('should handle a button click correctly', function() {
        // Create and render the component
        var component = Ext.create('MyApp.view.MyComponent', {
            renderTo: Ext.getBody()
        });
        // Get a reference to the button
        var button = component.down('button');
        // Simulate a button click
        spyOn(component, 'onButtonClick');
        button.fireEvent('click');
        // Verify that the onButtonClick method was called
        expect(component.onButtonClick).toHaveBeenCalled();
    });
});

In this example, a spy is set up on the onButtonClick method of the component using the spyOn function from Jasmine. Then, the fireEvent method is used to simulate a click on the button element within the component. Finally, the expect function is used to verify that the onButtonClick method was called as a result of the button click.

Testing Data Models

In addition to testing components, Sencha Test provides utilities for testing Ext JS and Sencha Touch data models.

Creating Test Data

Before you can test a data model, you often need to create test data. Sencha Test provides helper functions to simplify this process.

describe('My Model', function() {
    var data = {
        name: 'John Doe',
        age: 30,
        email: '[email protected]'
    };
    var model;
    beforeEach(function() {
        // Create a new instance of the model with test data
        model = Ext.create('MyApp.model.MyModel', data);
    });
    // Test cases go here
});

In this example, a data object is defined with test data. The beforeEach function is used to create a new instance of the MyApp.model.MyModel model with the test data before each test case is executed. This ensures that each test case has a fresh instance of the model to work with.

Testing Model Methods

Once you have an instance of the model with test data, you can test its methods and verify their behavior.

describe('My Model', function() {
    // Setup code...
    it('should have the correct name', function() {
        expect(model.get('name')).toBe('John Doe');
    });
    it('should validate email correctly', function() {
        expect(model.validateEmail()).toBe(true);
    });
});

In this example, the get method is used to retrieve the name field of the model instance, and the expect function is used to assert that it matches the expected value. Additionally, a custom validateEmail method is called, and its return value is verified using the expect function.

Testing Model Validations

Ext JS and Sencha Touch models often include built-in or custom validations. Sencha Test provides utilities for testing these validations.

describe('My Model', function() {
    // Setup code...
    it('should validate required fields', function() {
        model.set('name', '');
        expect(model.isValid()).toBe(false);
        expect(model.getValidation().name.isValid).toBe(false);
    });
});

In this example, the set method is used to set the name field to an empty string, which violates a required field validation. The isValid method is then called to check if the model is valid, and the expect function is used to assert that it returns false. Additionally, the getValidation method is used to access the validation result for the name field, and the expect function is used to assert that the validation failed.

Testing Stores

Ext JS and Sencha Touch applications often rely on stores to manage data. Sencha Test provides utilities for testing stores, including creating test data, testing store methods, and testing store events.

Creating Test Data

Similar to testing models, you often need to create test data before testing stores.

describe('My Store', function() {
    var data = [
        { name: 'John Doe', age: 30, email: '[email protected]' },
        { name: 'Jane Smith', age: 25, email: '[email protected]' },
        // Add more test data as needed
    ];
    var store;
    beforeEach(function() {
        // Create a new instance of the store with test data
        store = Ext.create('MyApp.store.MyStore', {
            data: data
        });
    });
    // Test cases go here
});

In this example, an array of test data objects is defined. The beforeEach function is used to create a new instance of the MyApp.store.MyStore store with the test data before each test case is executed.

Testing Store Methods

Once you have an instance of the store with test data, you can test its methods and verify their behavior.

describe('My Store', function() {
    // Setup code...
    it('should have the correct number of records', function() {
        expect(store.getCount()).toBe(2);
    });
    it('should filter records correctly', function() {
        store.filter('age', '>', 25);
        expect(store.getCount()).toBe(1);
        expect(store.getAt(0).get('name')).toBe('John Doe');
    });
});

In this example, the getCount method is used to verify the number of records in the store. Additionally, the filter method is used to apply a filter to the store, and the expect function is used to assert that the correct number of records is returned and that the remaining record has the expected value.

Testing Store Events

Stores often fire events when data changes, such as when records are added, updated, or removed. Sencha Test provides utilities for testing these events.

describe('My Store', function() {
    // Setup code...
    it('should fire the add event when a record is added', function() {
        var spy = jasmine.createSpy();
        store.on('add', spy);
        store.add({ name: 'New Record', age: 40, email: '[email protected]' });
        expect(spy).toHaveBeenCalled();
    });
});

In this example, a spy is created using the jasmine.createSpy function from Jasmine. The spy is then attached to the add event of the store using the on method. After adding a new record to the store using the add method, the expect function is used to assert that the spy was called, indicating that the add event was fired.

Testing Controllers

Ext JS and Sencha Touch applications often use controllers to handle application logic and events. Sencha Test provides utilities for testing controllers, including testing controller methods and testing controller events.

Testing Controller Methods

Similar to testing component methods, you can test the methods of a controller by calling them directly and verifying their behavior.

describe('My Controller', function() {
    var controller;
    beforeEach(function() {
        // Create a new instance of the controller
        controller = Ext.create('MyApp.controller.MyController');
    });
    it('should perform an action correctly', function() {
        var result = controller.doSomething();
        expect(result).toBe(expectedValue);
    });
});

In this example, the beforeEach function is used to create a new instance of the MyApp.controller.MyController controller before each test case is executed. The doSomething method is then called on the controller instance, and the expect function is used to assert that the return value matches the expected value.

Testing Controller Events

Controllers often listen for and handle events from other parts of the application. Sencha Test provides utilities for testing these event handlers.

describe('My Controller', function() {
    // Setup code...
    it('should handle an event correctly', function() {
        spyOn(controller, 'onEventHandler');
        // Simulate the event
        Ext.fireEvent('myEvent', data);
        expect(controller.onEventHandler).toHaveBeenCalledWith(data);
    });
});

In this example, a spy is set up on the onEventHandler method of the controller using the spyOn function from Jasmine. The Ext.fireEvent function is then used to simulate the myEvent event, passing in the data object as the event data. Finally, the expect function is used to verify that the onEventHandler method was called with the correct event data.

Asynchronous Testing

JavaScript code commonly uses asynchronous functions that require a different testing approach:

Using Asynchronous Code

Handling Timeouts

Await expected async operations within a timeout to prevent tests hanging:
it("saves async", () => {
    service.save(() => {});
    await waitFor(500);
    // assert
});

Running Unit Tests

Now that tests are written, they need to be executed. Sencha Test provides different options:

Command Line

Run all or specific tests via the sencha test command and Jasmine syntax.

Browser

Launch the Sencha Test app for an interactive test runner UI to debug.

Continuous Integration

Configure Sencha Test to run automatically on code changes with CI servers.
Test reports are generated in JUnit XML format for integration with build systems. Overall test execution and results can be monitored.

Unit testing also pairs well with test-driven development practices.

Test-Driven Development

TDD follows the recurring cycle:

  • Write a test for new functionality
  • Run all tests and see if the new test fails
  • Write the minimal code to pass the test
  • Refactor the code
  • Repeat

This ensures code is testable by design and tests validate each small incremental change.
Continuous Integration

CI Tools like Jenkins can be set up to run tests automatically on each code check-in using Sencha Test. This validates code quality objectively before code is merged or released.

Best Practices

Some tips for effective unit testing:

  • Keep tests isolated, focused, and fast
  • Use page objects to consolidate element references
  • Avoid duplication with shared test primitives
  • Organize tests into descriptive suites
  • Balance test coverage with code complexity
  • Automate everything possible for reliability

Conclusion

Sencha Test streamlines the process of unit testing Ext JS applications. Embracing testing makes applications more robust, maintainable, and prevents bugs from reaching production. Following best practices like TDD, continuous integration, and test automation delivers the greatest benefits. Overall, testing should be incorporated into workflows to improve development efficiency and code quality.

Interested in diving deeper into this topic? Be sure to read our companion article, which delves into Unit Testing For Applications in more detail. Additionally, check out our latest blog post for additional insights on Sencha WebTestIt —Our New, FREE Test Automation Tool.

Happy coding!

Recommended Articles

View More