nativescript-angular: NG11 Test failure : Error: zone-testing.js is needed for the async() test helper but could not be found.

Environment Provide version numbers for the following components (information can be retrieved by running tns info in your project folder or by inspecting the package.json of the project):

  • CLI: 7.1.0
  • Cross-platform modules: 7.1.0
  • Android Runtime:
  • iOS Runtime: 7.1.0
  • Plugin(s):
  • NativeScript-Angular: 11.0.0
  • Angular: 11.0

Describe the bug

ns test seem to be broken on a fresh nativescript angular project with “Error: zone-testing.js is needed for the async() test helper but could not be found.”

To Reproduce

I’ve created a project using : ns create --ng --id sample-ng sample-ng

Then, i’ve followed this documentation https://docs.nativescript.org/angular/tooling/testing/testing

The documentation has not been updated, some path are incorrect in setup.ts. My setup.ts

import '@nativescript/zone-js/testing.jasmine';
import {nsTestBedInit} from '@nativescript/angular/testing';
nsTestBedInit();

Then execute : ns test ios

The result :


NativeScript / 14.2 (14.2; iPhone) Renderer E2E executes events inside NgZone when listen is called outside NgZone FAILED
        Error: zone-testing.js is needed for the async() test helper but could not be found.
                Please make sure that your environment includes zone.js/dist/zone-testing.js
        error properties: Object({ originalStack: 'Error: zone-testing.js is needed for the async() test helper but could not be found.
                Please make sure that your environment includes zone.js/dist/zone-testing.js
            at new ZoneAwareError (file:///app/vendor.js:152354:33)
            at resetFakeAsyncZone (file:///app/vendor.js:77146:11)
            at UserContext.<anonymous> (file:///app/vendor.js:79298:9)
            at ZoneQueueRunner.attempt (eval at runTest (file:///app/vendor.js:149440:17), <anonymous>:7063:44)
            at ZoneQueueRunner.QueueRunner.run (eval at runTest (file:///app/vendor.js:149440:17), <anonymous>:7104:25)
            at runNext (eval at runTest (file:///app/vendor.js:149440:17), <anonymous>:7023:18)
            at next (eval at runTest (file:///app/vendor.js:149440:17), <anonymous>:7030:11)
            at eval (eval at runTest (file:///app/vendor.js:149440:17), <anonymous>:6924:9)
            at ZoneDelegate.push.../node_modules/@nativescript/zone-js/zone-nativescript.js.ZoneDelegate.invokeTask (file:///app/vendor.js:151477:31)
             ...
        Error: zone-testing.js is needed for the async() test helper but could not be found.
                Please make sure that your environment includes zone.js/dist/zone-testing.js
            at new ZoneAwareError (file:///app/vendor.js:152354:33)
            at resetFakeAsyncZone (file:///app/vendor.js:77146:11)
            at UserContext.<anonymous> (file:///app/vendor.js:79298:9)
            at <Jasmine>
            at ZoneDelegate.push.../node_modules/@nativescript/zone-js/zone-nativescript.js.ZoneDelegate.invokeTask (file:///app/vendor.js:151477:31)
            at Zone.push.../node_modules/@nativescript/zone-js/zone-nativescript.js.Zone.runTask (file:///app/vendor.js:151244:47)
            at drainMicroTaskQueue (file:///app/vendor.js:151651:35)
        zone-testing.js is needed for the async() test helper but could not be found. Please make sure that your environment includes zone.js/dist/zone-testing.js thrown


About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 17
  • Comments: 19 (1 by maintainers)

Most upvoted comments

Not sure if it will be useful in you case but I just fixed a similar error by changing the import order of zone-testing in my src/test.ts file :

import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';

Not sure if it will be useful in you case but I just fixed a similar error by changing the import order of zone-testing in my src/test.ts file :

import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';

Actually thanks for that hint… never thought it could be the order of imports… Just as a small addition to this.

The import of zone-testing has to be the very first of all imports. Line 1 - import 'zone.js/dist/zone-testing';

Add this to setup.Jest.ts file in the root folder:

import 'zone.js';
import 'zone.js/testing';

Not sure if it will be useful in you case but I just fixed a similar error by changing the import order of zone-testing in my src/test.ts file :

import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';

Actually thanks for that hint… never thought it could be the order of imports… Just as a small addition to this.

The import of zone-testing has to be the very first of all imports. Line 1 - import 'zone.js/dist/zone-testing';

it worked, but is there any explanation for this?

Not sure if this helps, but I was able to get my NS7/NG11 app to run Jasmine/Karma unit tests. I had to do some serious package testing (dependency validation) to see what works, but all good.

package.json

{
  "description": "NativeScript Mobile Template",
  "license": "SEE LICENSE",
  "repository": "<fill-your-repository-here>",
  "dependencies": {
    "@angular/animations": "11.2.6",
    "@angular/common": "11.2.6",
    "@angular/compiler": "11.2.6",
    "@angular/core": "11.2.6",
    "@angular/forms": "11.2.6",
    "@angular/platform-browser": "11.2.6",
    "@angular/platform-browser-dynamic": "11.2.6",
    "@angular/router": "11.2.6",
    "@nativescript/angular": "11.0.1",
    "@nativescript/core": "7.3.0",
    "@nativescript/theme": "3.0.1",
    "@nativescript/unit-test-runner": "1.0.2",
    "@nativescript/webpack": "3.0.9",
    "base-64": "1.0.0",
    "crypto-js": "4.0.0",
    "reflect-metadata": "0.1.13",
    "rxjs": "6.6.6",
    "zone.js": "0.11.4"
  },
  "devDependencies": {
    "@angular/compiler-cli": "11.2.6",
    "@nativescript/android": "7.0.1",
    "@nativescript/ios": "7.2.0",
    "@nativescript/types": "7.3.0",
    "@ngtools/webpack": "11.2.5",
    "@types/jasmine": "3.6.7",
    "istanbul-instrumenter-loader": "3.0.1",
    "jasmine-core": "3.6.0",
    "jasmine-spec-reporter": "6.0.0",
    "karma": "6.1.2",
    "karma-coverage": "2.0.3",
    "karma-coverage-istanbul-reporter": "3.0.3",
    "karma-jasmine": "4.0.1",
    "karma-nativescript-launcher": "0.4.0",
    "karma-sourcemap-loader": "0.3.8",
    "karma-spec-reporter": "0.0.32",
    "karma-typescript": "5.5.0",
    "karma-webpack": "3.0.5",
    "typescript": "4.0.2"
  },
  "main": "main.js"
}

karma.config.js

module.exports = function (config) {
  const options = {
    basePath: '',
    frameworks: ['jasmine'],

    plugins: [
      'karma-jasmine',
      'karma-coverage-istanbul-reporter',
      'karma-coverage',
      'karma-webpack',
      'karma-nativescript-launcher',
      'karma-sourcemap-loader',
      'karma-spec-reporter'
    ],

    files: [
      'src/tests/setup.ts',
      'src/tests/**/*.spec.ts',
      'src/app/**/*.ts'
    ],

    exclude: [
    ],

    preprocessors: {
      'src/tests/setup.ts': ['webpack'],
      'src/tests/**/*.spec.ts': ['webpack'],
      'src/app/**/*.ts': ['webpack', 'coverage']
    },

    reporters: ['progress', 'coverage', 'coverage-istanbul'],

    coverageIstanbulReporter: {
      dir: require('path').join(__dirname, 'coverage'),
      reports: ['html', 'lcov', 'text-summary'],
      fixWebpackSourcePaths: true
    },

    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: [],
    singleRun: false,

    customLaunchers: {
      android: {
        base: 'NS',
        platform: 'android'
      },
      ios: {
        base: 'NS',
        platform: 'ios'
      },
      ios_simulator: {
        base: 'NS',
        platform: 'ios',
        arguments: ['--emulator']
      }
    }
  };

  setWebpack(config, options);

  config.set(options);
}

function setWebpack(config, options) {
  if (config && config.bundle) {
    const env = {};
    env[config.platform] = true;
    env.sourceMap = config.debugBrk;
    env.appPath = config.appPath;
    options.webpack = require('./webpack.config')(env);
    options.webpack.module.rules.push(
      {
        test: /\.ts$/,
        use: {
          loader: "istanbul-instrumenter-loader",
          options: {
            esModules: true
          }
        },
        enforce: 'post'
      }
    );
    delete options.webpack.entry;
    delete options.webpack.output.libraryTarget;
    const invalidPluginsForUnitTesting = ["GenerateBundleStarterPlugin", "GenerateNativeScriptEntryPointsPlugin"];
    options.webpack.plugins = options.webpack.plugins.filter(p => !invalidPluginsForUnitTesting.includes(p.constructor.name));
  }
}

setup.ts (customized and placed in the root of the ‘tests’ folder):

import '@nativescript/zone-js';
import 'zone.js/dist/zone-testing';

import { TestBed } from '@angular/core/testing';
import { platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
import { BrowserTestingModule } from '@angular/platform-browser/testing';

export function customNsTestBedInit(): void {
  TestBed.initTestEnvironment(
    BrowserTestingModule,
    platformBrowserDynamicTesting()
  );
}

export function customNsTestBedBeforeEach(
  components: any[],
  providers: any[] = [],
  imports: any[] = [],
  entryComponents: any[] = []) {
  return (done: () => void) => {
    if (entryComponents.length === 0) {
      TestBed.configureTestingModule({
        declarations: [ ...components ],
        providers: [ ...providers ],
        imports: [ ...imports ]
      });
    }
    TestBed.compileComponents()
      .then(() => done())
      .catch((e) => {
        console.log(`Failed to instantiate test component with error: ${e}`);
        console.log(e.stack);
        done();
      });
  };
}

export function customNsTestBedAfterEach(resetEnv = true, resetFn = customNsTestBedInit): void {
  TestBed.resetTestingModule();
  if (resetEnv) {
    TestBed.resetTestEnvironment();
    resetFn();
  }
}

// Note. This testing infrastructure can test components but it
// can only test the functions. No rendering of the dom is done
// Nativescript runs its own zone.js which intereferes with the
// zone.js code run by NG10. So my test suite completely disables
// the nativescript code and it should not execute anywhere.
// ** In testing if you inject something that calls any nativescript
// code it will reactivate the nativescript zone.js. make sure to stub
// this out so that it does not interfere with the unit test.


/// intialize Test bed.
customNsTestBedInit();

feedback.component.spec.ts (a spec file with a few tests)

import { TestBed } from '@angular/core/testing';
import { customNsTestBedAfterEach, customNsTestBedBeforeEach } from '../setup';

import { Page } from '@nativescript/core';
import { RouterTestingModule } from '@angular/router/testing';

import { FeedbackComponent } from "~/app/common/feedback/feedback.component";

describe('Initialize', () => {
  beforeEach(
    customNsTestBedBeforeEach(
      [],
      [
        FeedbackComponent,
        Page
      ],
      [
        RouterTestingModule
      ]
    )
  );

  afterEach(() => customNsTestBedAfterEach());

  describe('Component Tests -', () => {
    let component: FeedbackComponent;

    beforeEach(() => {
      component = TestBed.inject(FeedbackComponent);
    });

    describe('FeedbackComponent', () => {
      it('should resolve component', () => {
        expect(component).toBeTruthy();
      });

      it('should initialize variables', () => {
        expect(component.isTablet).toBeDefined();
        expect(component.timer).toBeNull();
      });

      it('should call showFeedback()', () => {
        spyOn(component, 'showFeedback');
        component.showFeedback("test");
        expect(component.showFeedback).toHaveBeenCalledWith('test');
      });

      it('should verify showFeedback() parameters', () => {
        spyOn(Page.prototype, 'getViewById').and.callThrough();
        component.showFeedback("test");
        expect(Page.prototype.getViewById).toHaveBeenCalledWith('feedbackLayout');
        expect(Page.prototype.getViewById).toHaveBeenCalledWith('feedbackFrame');
        expect(Page.prototype.getViewById).toHaveBeenCalledWith('feedbackMessage');
        expect(Page.prototype.getViewById).toHaveBeenCalledWith('feedbackClose');
      });

      it('should call hideFeedback()', () => {
        spyOn(component, 'hideFeedback');
        component.hideFeedback();
        expect(component.hideFeedback).toHaveBeenCalled();
      });

      it('should verify hideFeedback() parameters', () => {
        spyOn(Page.prototype, 'getViewById');
        component.hideFeedback();
        expect(Page.prototype.getViewById).toHaveBeenCalledWith('feedbackLayout');
      });
    });
  });
});