Running selected tests or testcases in Jasmine

23 Sep 2017

Jasmine is a behaviour driven test development framework, allows a clean and easy syntax to write test cases. It doesn't require DOM access, so it can be used in non-browser environment like nodejs. 

Karma runner allows testcases to be run on browser like chrome, firefox, edge, safari, phantomjs etc., using various launchers listed in its official page. Thus it allows running  test cases that requires DOM manipulations & validations. 

For Angular application development, using Karma + Jasmine is a great combo for writing testcases. It offers great flexibility to test the Angular components and its view templates. These test cases usually runs faster. But in Behaviour Driven Development (BDD), the number of test cases written will grow exponentially as the project development continues. This will cause frustrations while writing test cases, due to repeated recompile and long test running times.

To speedup the test case development, we may consider

Option 1:

Modifying the GLOB patterns to select just the required specs files to watch for the specific change. As you continue developing another module/component, it requires modification for covering just those. This frequent manual modification to GLOB patters in Karma config and reverting it back before any code check-in is a tiresome process.

Many framework like Jest offers you to dynamically filter out the testcase on the fly and run just those cases in the watch mode. This is a cool, good to have feature that is currently not available in Jasmine or Karma runner.

Note: Jest uses JsDom to run its testcases which will be faster for running test cases. But if there are any external component in your test subject that uses CSS DOM styling properties or some low level JS object will result in 'object undefined' error that may not be easily poly filled.
e.g., If you need to open a modal window and test values on it. It will probably throw an error, as the modal component may try to set some CSS properties for animation.

So for above test scenarios, Karma runner will be the best pick or fit as it uses actual browsers. 

Option 2: 

Though Jasmine framework lacks filtering of testcases at the runtime in watch mode, it does offer an alternate way to just test the cases that we are currently focused on.

A sample Jasmine test case for Angular project is as follows

describe('scrollable-tabset', () => {
    beforeEach(
        () => { TestBed.configureTestingModule({ declarations: [TestComponent], imports: [ScrollableTabsModule.forRoot()] }); });

    it('should initialize inputs with default values', async(() => {
        const defaultConfig = new ScrollableTabsetConfig();
        const tabset = new ScrollableTabsetComponent(new ScrollableTabsetConfig());
        expect(tabset.type).toBe(defaultConfig.type);
    }));

    it('', ()=>{});

    it('', ()=>{});


    // ...


    it('should render tabs and select first tab as active by default', async(() => {
        const fixture = createTestComponent(`
      <scrollable-tabset>
        <scrollableTab title="foo"><ng-template scrollableTabContent>Foo</ng-template></scrollableTab>
        <scrollableTab title="bar"><ng-template scrollableTabContent>Bar</ng-template></scrollableTab>
      </scrollable-tabset>
    `);

        const tabTitles = getTabTitles(fixture.nativeElement);
        const tabContent = getTabContent(fixture.nativeElement);

        expect(tabTitles[0].textContent).toMatch(/foo/);
        expect(tabTitles[1].textContent).toMatch(/bar/);
        expect(tabContent.length).toBe(1);
        expect(tabContent[0].textContent).toMatch(/Foo/);

        expectTabs(fixture.nativeElement, [true, false]);
    }));
 });
So if you want to run a particular test case i.e., the 100th test case or set of test cases written for a specific component. You can use focused test execution feature of Jasmine framework, just modify
  1. describe to fdescribe, which will run all the test cases under a specific feature/component.
  2. it to fit, which will execute just that specific test case. 
 
fdescribe('scrollable-tabset', () => {
    beforeEach(
        () => { TestBed.configureTestingModule({ declarations: [TestComponent], imports: [ScrollableTabsModule.forRoot()] }); });

    it('should initialize inputs with default values', async(() => {
        const defaultConfig = new ScrollableTabsetConfig();
        const tabset = new ScrollableTabsetComponent(new ScrollableTabsetConfig());
        expect(tabset.type).toBe(defaultConfig.type);
    }));

    it('', ()=>{});

    it('', ()=>{});


    // ...


    fit('should render tabs and select first tab as active by default', async(() => {
        const fixture = createTestComponent(`
      <scrollable-tabset>
        <scrollableTab title="foo"><ng-template scrollableTabContent>Foo</ng-template></scrollableTab>
        <scrollableTab title="bar"><ng-template scrollableTabContent>Bar</ng-template></scrollableTab>
      </scrollable-tabset>
    `);

        const tabTitles = getTabTitles(fixture.nativeElement);
        const tabContent = getTabContent(fixture.nativeElement);

        expect(tabTitles[0].textContent).toMatch(/foo/);
        expect(tabTitles[1].textContent).toMatch(/bar/);
        expect(tabContent.length).toBe(1);
        expect(tabContent[0].textContent).toMatch(/Foo/);

        expectTabs(fixture.nativeElement, [true, false]);
    }));
 });
?

With the above f(focused) test cases, we can efficiently cut out development time spent on executing & waiting for the results of un-related test cases.

Happy coding Jasmine test cases wink !

 

Additional Note
If you are concerned about these fdescribe and fit getting into the actual code check-ins and bypassing the test execution during CI(Continuous Integrations) builds. Then you can write a safety task to replace all the fdescribe and fit into describe and it, and then schedule your actual testing task. A sample node script for doing that is as follows

/**
 * Jasmine unit testcase framework has a wonderful feature to run just the specific test cases or test
 * using fdescribe() or fit(). Following script replaces the focused test keywords to normal
 * to prevent accidental skipping of testcase execution on CI build server
 */
const helpers = require('../webpack/helpers');
const glob = require('glob');
const fs = require('fs');

const root = helpers.root('./');

glob(root + '**/ClientApp/**/*.spec.ts', (err, specs) => {
    if (err) {
        throw new Error(err);
    }

    specs.forEach((spec) => {
        fs.readFile(spec, 'utf8', (fileErr, data) => {
            // Just skip if a file has error reading it
            // Hopefully it cause trouble with the VSTS build and will fail as expected
            if (!fileErr) {
                let formattedData = data.replace(/\b(fdescribe)\s*\(\s*'\s*\b/g, 'describe(\'');
                formattedData = formattedData.replace(/\b(fit)\s*\(\s*'\s*\b/g, 'it(\'');
                if (data !== formattedData) {
                    fs.writeFile(spec, formattedData, () => {
                        console.log('Focused test present in ' + spec);
                    });   
                }
            }
        });
    });
});