angular / angular

Deliver web apps with confidence πŸš€
https://angular.dev
MIT License
96.02k stars 25.39k forks source link

RC6 unit tests with mocha fail to run #11230

Closed mattlewis92 closed 7 years ago

mattlewis92 commented 8 years ago

I'm submitting a ... (check one with "x")

[*] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

No tests run. It was working fine in RC5 and all breaking changes have been addressed. The error given is:

Expected to be running in 'ProxyZone', but it was not found.
assertPresent@test.ts:6620:98
resetFakeAsyncZone@test.ts:25346:37
test.ts:26216:32

This is thrown before the it block is executed. Switching from mocha to jasmine + adding the zone jasmine patch fixes the issue as a workaround.

Expected/desired behavior Tests should run with mocha

Reproduction of the problem

I've put together a repo which reproduces the error here: https://github.com/mattlewis92/angular2-testing-bug/blob/master/test.ts

What is the motivation / use case for changing the behavior?

N/A

Please tell us about your environment:

qdouble commented 8 years ago

My imports in my spec-bundle.js file look like this and work fine:

require('zone.js/dist/zone');;
require('zone.js/dist/long-stack-trace-zone');
require('zone.js/dist/async-test');
require('zone.js/dist/fake-async-test');
require('zone.js/dist/sync-test');
require('zone.js/dist/proxy');
require('zone.js/dist/jasmine-patch');
ocombe commented 8 years ago

yes with the zone.js update you have to add require('zone.js/dist/proxy'); to make tests work now

mattlewis92 commented 8 years ago

I already did that: https://github.com/mattlewis92/angular2-testing-bug/blob/master/test.ts#L7

mattlewis92 commented 8 years ago

Ah on further digging it's because I'm using mocha and not jasmine + the jasmine zone patch

lacolaco commented 8 years ago

@juliemr What do you think about this?

IgorMinar commented 8 years ago

don't use jasmine-patch with mocha. there is a reason why it's called "jasmine-patch" :)

we haven't looked into running the tests with mocha yet. the required change is that we need to intercept when mocha and ensure that tests run in the right zones - similar to how jasmine-patch does it for jasmine: https://github.com/angular/zone.js/blob/master/lib/jasmine/jasmine.ts

mattlewis92 commented 8 years ago

Agreed that we need something similar like a mocha-patch πŸ˜€ I'm not familiar enough with how zones work, but is there not a way to expose a generic API to create and destroy a test zone in the test frameworks tear up/down methods that doesn't involve patching the test framework?

On 2 Sep 2016, at 23:01, Igor Minar notifications@github.com wrote:

don't use jasmine-patch with mocha. there is a reason why it's called "jasmine-patch" :)

we haven't looked into running the tests with mocha yet. the required change is that we need to intercept when mocha and ensure that tests run in the right zones - similar to how jasmine-patch does it for jasmine: https://github.com/angular/zone.js/blob/master/lib/jasmine/jasmine.ts

β€” You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.

IgorMinar commented 8 years ago

jasmine tries to create an illusion that each test/spec runs in a new turn of the event loop when it doesn't. this is why it needs to be patched.

I'm not sure about how much patching will mocha require but I would expect that it's going to be similar - it all depends on how the test scheduler is implemented.

On Fri, Sep 2, 2016 at 3:21 PM Matt Lewis notifications@github.com wrote:

Agreed that we need something similar like a mocha-patch πŸ˜€ I'm not familiar enough with how zones work, but is there not a way to expose a generic API to create and destroy a test zone in the test frameworks tear up/down methods that doesn't involve patching the test framework?

On 2 Sep 2016, at 23:01, Igor Minar notifications@github.com wrote:

don't use jasmine-patch with mocha. there is a reason why it's called "jasmine-patch" :)

we haven't looked into running the tests with mocha yet. the required change is that we need to intercept when mocha and ensure that tests run in the right zones - similar to how jasmine-patch does it for jasmine: https://github.com/angular/zone.js/blob/master/lib/jasmine/jasmine.ts

β€” You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.

β€” You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/angular/angular/issues/11230#issuecomment-244500750, or mute the thread https://github.com/notifications/unsubscribe-auth/AANM6EPxLoQWmmBhEpnWNQWrofD_W8dpks5qmKFqgaJpZM4JyepH .

lacolaco commented 8 years ago

I think this change is a regression. We were really excited in RC.4 when we've been able to use Mocha with A2. Please solve this in 2.0 Final...

ocombe commented 8 years ago

I don't think this will be for final, they are on a tight schedule, but probably for later

mattlewis92 commented 8 years ago

@laco0416 as mocha and jasmine have a fairly similar API for basic features like describe, it blocks etc, it's pretty easy to switch to jasmine temporarily until the issue gets fixed e.g. https://github.com/mattlewis92/angular2-calendar/commit/866ef94c648eb27deaed15da7593ad4b1c552e5e

I'd also imagine it's possible now that through some source code reading of mochas internal APIs (here maybe?), a third party zone patch for mocha could be written that does a similar job of the jasmine patch.

lacolaco commented 8 years ago

@matthewjh uh... that's because you use chai and expect. All my projects use assert.

mattlewis92 commented 8 years ago

Ah ok, to the best of my understanding assert is still test runner independent though isn't it and should still work with jasmine?

lacolaco commented 8 years ago

@mattlewis92 uh-oh, I was under the impression. you're right. ok, I could switch mocha into jasmine temporary, just temporary. Thanks.

lacolaco commented 8 years ago

The problem we should solve is that A2 is depending on a specific testing framework by the default.

IgorMinar commented 8 years ago

Angular doesn't depend on a specific testing framework. Testing frameworks however are not compatible with zone.js out of the box because they implement their own task scheduler in a way that doesn't mimic the browser scheduler. In fact if you don't use async or fakeAsync helpers then you can write tests in mocha.

If you want async and fakeAsync goodness then you need to patch the test runner so that the tasks created from tests are executed correctly.

On Tue, Sep 6, 2016 at 12:48 AM Suguru Inatomi notifications@github.com wrote:

The problem we should solve is that A2 is depending on a specific testing framework by the default.

β€” You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/angular/angular/issues/11230#issuecomment-244875087, or mute the thread https://github.com/notifications/unsubscribe-auth/AANM6OXoieFsyTy1sX0QzwiyqvsPvNx9ks5qnRrhgaJpZM4JyepH .

lacolaco commented 8 years ago

In fact if you don't use async or fakeAsync helpers then you can write tests in mocha.

No. Even when I use only describe and it , it fails.

import 'core-js';
import 'zone.js/dist/zone';
import 'zone.js/dist/long-stack-trace-zone';
import 'rxjs';
import {
  TestBed
} from '@angular/core/testing';
import {
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
import {assert} from 'chai';

TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());

describe('test demo', () => {
  it('should ok', () => {
    console.log('0');
    assert({ foo: 'foo' }.foo === { foo: 'foo' }.foo);
  });
});
PhantomJS 2.1.1 (Mac OS X 0.0.0)  "before each" hook for "should ok" FAILED
    undefined is not an object (evaluating 'ProxyZoneSpec.assertPresent')
    resetFakeAsyncZone@test.ts:27523:23
    test.ts:28393:32
PhantomJS 2.1.1 (Mac OS X 0.0.0): Executed 1 of 1 (1 FAILED) ERROR (0.013 secs / 0.007 secs)

Then I added import 'zone.js/dist/proxy';, but it still failed.

PhantomJS 2.1.1 (Mac OS X 0.0.0)  "before each" hook for "should ok" FAILED
    Expected to be running in 'ProxyZone', but it was not found.
    assertPresent@test.ts:9455:98
    resetFakeAsyncZone@test.ts:27686:37
    test.ts:28556:32
PhantomJS 2.1.1 (Mac OS X 0.0.0): Executed 1 of 1 (1 FAILED) ERROR (0.012 secs / 0.007 secs)
npm ERR! Test failed.  See above for more details.

Also, import 'zone.js/dist/sync-test'; didn't solve anything.

tjoskar commented 8 years ago

@laco0416, I'm writing mocha test for a small library and don't need async or fakeAsync so I skip the whole TestBed.initTestEnvironment which works fine.

require('core-js/es7/reflect');
require('zone.js/dist/zone');
require('zone.js/dist/long-stack-trace-zone');
Error.stackTraceLimit = 5;
StefH commented 8 years ago

@tjoskar You also do not use Injection ?

And when I only use your requires, I get this error message when building:

node_modules\zone.js\dist\zone.js:113 var setNative = utils_1.patchMethod(window.XMLHttpRequest.prototype, 'send', function () { return function (self, args) { ^

TypeError: Cannot read property 'prototype' of undefined

tjoskar commented 8 years ago

@StefH, That is correct, I don't need Angular's Injection for this project.

I'm using webpack, mocha and karma. This is my endpoint for the unit test: https://github.com/tjoskar/ng2-lazyload-image/blob/master/unit-test.bundle.js

And are then letting webpack/karma to find all my unittest in this file: https://github.com/tjoskar/ng2-lazyload-image/blob/master/unit-test.karma.bundle.js

gitawego commented 8 years ago

in released version, this bug's still there. any update about this bug ? is there anything like 'zone.js/dist/mocha-patch' instead of 'zone.js/dist/jasmine-patch' ?

lacolaco commented 8 years ago

Currently, zone.js cannot have mocha-patch because the sources of zone.js are written in TypeScript and global objects (describe, it and more) are typed in Jasmine's definition.

@types/jasmine cannot co-exist with @types/mocha.

so, mocha-patch have to be provided outside of zone.js. I don't know any 3rd-party like that.

StefH commented 8 years ago

I gave up on mocha, currently using karma with webpack. All is fine, except cannot debug using visual studio code.

So if anyone has this working:

Please advice!

Sleepyowl commented 8 years ago

I got it working somehow using "interfaces" extension feature of Mocha. Refer to this gist for details.

It's a quick initial try with some missing bits, but it works with my tests. The biggest missing part is a counterpart to jasmine QueueRunner patching. Once I figure out how to deal with it, I will put this to an npm module, I guess.

frederikschubert commented 8 years ago

Doing the following after calling TestBed.initTestEnvironment is also a viable workaround if one doesn't use async or fakeAsync:

import { getTestBed } from "@angular/core/testing";

const hook = new Mocha.Hook("Modified Angular beforeEach Hook", () => {
    getTestBed().resetTestingModule();
});
hook.ctx = mocha.suite.ctx;
hook.parent = mocha.suite;
mocha.suite._beforeEach = [hook];
nording commented 7 years ago

There is a Mocha-patch available, https://github.com/angular/zone.js/tree/master/lib/mocha, it works well with the test utils like async and fakeAsync.

IgorMinar commented 7 years ago

We'll be cutting a release of zone.js within the next few days. Once that happens you'll be able to get the patch from the npm package then.

On Wed, Nov 16, 2016 at 9:55 AM nording notifications@github.com wrote:

There is a Mocha-patch available, https://github.com/angular/zone.js/tree/master/lib/mocha, it works well with the test utils like async and fakeAsync.

β€” You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/angular/angular/issues/11230#issuecomment-261020454, or mute the thread https://github.com/notifications/unsubscribe-auth/AANM6FO2nyz7K-JTJOW9dQ9ujXrCMHn8ks5q-0OHgaJpZM4JyepH .

mattlewis92 commented 7 years ago

Confirmed that the latest zone.js version mocha patch works perfectly, nice work @nording !

HichamBI commented 7 years ago

Hello,

I'm trying to test my angular2 app using mocha and webpack and async tools. I've imported the mocha-patch.js and declared globals for mocha :

Here is my mocha-shim.js :

var jsdom = require('jsdom');
var document = jsdom.jsdom('<!doctype html><html><body></body></html>');
var window = document.defaultView;

global.document = document;
global.HTMLElement = window.HTMLElement;
global.XMLHttpRequest = window.XMLHttpRequest;

require('core-js/es6');
require('core-js/es7/reflect');
require('zone.js/dist/zone');
require('zone.js/dist/long-stack-trace-zone');
require('zone.js/dist/proxy');
require('zone.js/dist/sync-test');
require('zone.js/dist/mocha-patch');
require('zone.js/dist/async-test');
require('zone.js/dist/fake-async-test');

var testing = require('@angular/core/testing');
var browser = require('@angular/platform-browser-dynamic/testing');

testing.TestBed.initTestEnvironment(browser.BrowserDynamicTestingModule, browser.platformBrowserDynamicTesting());

But when I run mocha-webpack I'm getting the following error :

ReferenceError: window is not defined on mocha-patch.js:146:4

And if I declare window globaly, I'm getting the following error :

var zoneSymbol = Zone['__symbol__'];
ReferenceError: Zone is not defined

I guess am doing something wrong, but I don't know where ...

Can anyone help ?

Thanks.

mattlewis92 commented 7 years ago

You probably want phantomjs + karma to run your mocha tests rather than using mocha directly in node

HichamBI commented 7 years ago

No, I don't want to use karma and phantomjs if it possible. I want also understand why it not working with the new Zone.js release.

mattlewis92 commented 7 years ago

Ah I'm not sure then, I'm not 100% sure if this is a supported use case, I think you're meant to use universal if running angular on node. You might be able to get better support on stackoverflow.

HichamBI commented 7 years ago

I run angular with webpack correctly, my problem is with the new mocha-patch, I want just to know how to use it.

Testing with mocha and webpack work well if I don't use async tools :

http://www.radzen.com/blog/testing-angular-webpack-mocha/

I'll try on stackoverflow also. Thanks.

Sleepyowl commented 7 years ago

Hey, @HichamBI I'm using mocha with jsdom without any issues. Though I don't use the "official" patch, but rather my own -- see my earlier comment in this thread.

I also copy properties from jsdom's window to global and set global.window -- everything before requiring zone.js and company.

Hope this helps.

HichamBI commented 7 years ago

Hi @Sleepyowl and thanks for your answer.

But how do you require zone.js after declaring global.window without getting an error ? Because, in my case, if I do that, I'm getting automatically this error :

var zoneSymbol = Zone['__symbol__'];
ReferenceError: Zone is not defined

even if I use the new mocha-patch or not ...

Thanks again.

Sleepyowl commented 7 years ago

@HichamBI Looking at my code: zone.js version is 0.6.26 require section:

require("reflect-metadata");
require('zone.js/dist/zone');
require('zone.js/dist/long-stack-trace-zone');
require('zone.js/dist/async-test');
require('zone.js/dist/fake-async-test');
require('zone.js/dist/sync-test');
require('zone.js/dist/proxy');

Another thing to note is that I do it in the custom mocha runner, not in tests.

nording commented 7 years ago

@HichamBI I have done some digging in this, it look like your mocha-shim is running before Mocha is loaded. The functions that is suppose to be exported on the global context is not available when the mocha-shim is executed.

The reason you get: var zoneSymbol = Zone['__symbol__']; ReferenceError: Zone is not defined when you try to assign global.window = global, look like it's because of the same reason (too early).

I moved the patching into the " -entry" file created when I executed mocha-webpack (located in /.tmp/mocha-webpack/*-entry.js), and then I didn't get any errors. The top of the file look like this: Mocha = require('mocha/lib/mocha'); window = global; require('zone.js/dist/mocha-patch');

I don't know mocha-webpack that well, so probably a better place to put this, but for the test I did this. I tried to put in the "mocha-webpack.opts" file, but that was also too early.

HichamBI commented 7 years ago

Thanks @nording for your answer,

Yes you're right, placing global on mocha-shim is to early, and even if requiring Mocha and mocha-patch.js in the */.tmp/mocha-webpack/-entry.js** file, the Proxy Zone is not initialized correctly, I still getting :

Expected to be running in 'ProxyZone', but it was not found.

Using mocha-patch.js like this way (with mocha-webpack) is not working for the moment.

Thanks again.

HichamBI commented 7 years ago

Finally I tests my Angular 2 application like this way : http://hichambi.github.io/2016/12/27/testing-angular2-with-webpack-mocha-on-browser-and-node.html.

The mocha-patch work fine when I use the mocha-loader webpack plugin. (Browser testing) For tests In Node, I used jsdom, without mocha-patch. Tests are working fine right now.

Thanks a lot for your help.

mikechamberlain commented 7 years ago

As this is currently the first google result for "Expected to be running in 'ProxyZone', but it was not found", I'll offer a solution unrelated to the report above.

I stupidly had my fakeAsync() call around the describe callback block instead of the it callback. Took me a little head scratching to figure it out, so maybe this will save others some time.

mmc41 commented 7 years ago

I put all my angular 4 services and tests in a seperate npm and got them to run in the node console (outside browser) using jsdom (in a way similar as described here but without complications of webpack). Much faster and nicer experience to develop and debug :-). The only drawback is that I can't use async because zone's mocha-patch.js does not work with jsdom/node (so I have to use the done() approach).

@IgorMinar Would be nice if your mocha-patch could work in node as well for such scenarios?

andrewvmail commented 7 years ago

Hi @mmc41 can you explain how did you get it to run without webpack? seperate npm?

mmc41 commented 7 years ago

@andrewvmail Angular code does not need web pack to run. I use ts-node together with a "magic" script that setup jsdom to act as a browser so the angular services can run outside the browser.

andrewvmail commented 7 years ago

@mmc41 yep thanks i figured it out. just had to shim node with some stuff. I put this in setup.js and run the test like so mocha -r test/setup.js --compilers js:babel-register src/views/angular/angular.test.js

code is for jsdom 9.9.1 i think for newer one its different syntax.

require('core-js/es6')
require('core-js/es7/reflect')
require('zone.js/dist/zone-node')
require('zone.js/dist/long-stack-trace-zone')
require('zone.js/dist/proxy')
require('zone.js/dist/sync-test')
require('zone.js/dist/async-test')
require('zone.js/dist/fake-async-test')
require('zone.js/dist/proxy')

const testing = require('@angular/core/testing')
const browser = require('@angular/platform-browser-dynamic/testing')

testing.TestBed.initTestEnvironment(
  browser.BrowserDynamicTestingModule,
  browser.platformBrowserDynamicTesting()
)

const jsdom = require('jsdom')

global.document = jsdom.jsdom('<!doctype html><html><body></body></html>')
global.window = document.defaultView
global.navigator = { userAgent: 'node.js' }
global.CustomEvent = global.window.CustomEvent = () => {}
global.window.dispatchEvent = () => {}
andrewvmail commented 7 years ago

that said i couldn't get angular dep injection working with the test setup like below. so i just cheat and manually assign those from the outside...

...
TestBed.configureTestingModule({
        declarations: [TestComponent],
        providers: [
          ChangeDetectorRef,
          {
            provide: SomeService,
            useFactory: () => {
              return new SomeService(initialStore)
            },
          },
        ],
      }).compileComponents()
...

and in the component

...
export class TestComponent {
  constructor(
    @Inject(SomeService) service: SomeService,
    @Inject(ChangeDetectorRef) ref: ChangeDetectorRef
    // why doesn't above work?
  ) {}
}
shcallaway commented 6 years ago

I am trying to use Karma with Mocha, but it crashes after running a single test due to a zone error:

START:

Finished in 0 secs / 0 secs @ 14:54:15 GMT-0800 (PST)

SUMMARY:
βœ” 1 test completed
LOG: 'Current zone: <root>'
LOG: 'Current zone: <root>'
  AccountInfoComponent
    βœ” should do account thing
LOG: 'Current zone: ProxyZone'
LOG: 'Current zone: ProxyZone'
HeadlessChrome 0.0.0 (Mac OS X 10.12.6) ERROR
  Uncaught Error: Unexpected zone: ProxyZone
  at http://localhost:9876/_karma_webpack_/polyfills.bundle.js:10671
HeadlessChrome 0.0.0 (Mac OS X 10.12.6) ERROR
  Uncaught Error: Unexpected zone: ProxyZone
  at http://localhost:9876/_karma_webpack_/polyfills.bundle.js:10671

I added the LOGs. It appears the exception is being thrown by mocha-patch.js.

Here is my test shim:

// The order of these three imports matters
import "zone.js/dist/proxy";
import "zone.js/dist/sync-test";
import "zone.js/dist/mocha-patch";

// But the order of these does not
import "zone.js/dist/async-test";
import "zone.js/dist/fake-async-test";
import "zone.js/dist/long-stack-trace-zone";

import { getTestBed } from "@angular/core/testing";
import {
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting
} from "@angular/platform-browser-dynamic/testing";

declare const __karma__: any;
declare const require: any;

// Prevent Karma from running prematurely
__karma__.loaded = function() {};

// Initialize the Angular testing environment
getTestBed().initTestEnvironment(
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting()
);

// Require spec files
const context = require.context("./", true, /\.spec\.ts$/);
context.keys().map(context);

// Start Karma
__karma__.start();

If I remove this block from mocha-patch.js the entire suite runs and passes:

if (Zone.current !== rootZone) {
    throw new Error('Unexpected zone: ' + Zone.current.name);
}

Any idea what is happening here? Is this check necessary?

basickarl commented 5 years ago

Well I'm now bumping into this.

Error: Missing Mocha.js
    at /home/karl/dev/MOP/node_modules/zone.js/dist/mocha-patch.js:21:19
    at /home/karl/dev/MOP/node_modules/zone.js/dist/mocha-patch.js:141:7
    at /home/karl/dev/MOP/node_modules/zone.js/dist/mocha-patch.js:8:9
    at Object.<anonymous> (/home/karl/dev/MOP/node_modules/zone.js/dist/mocha-patch.js:9:2)
    at Module._compile (internal/modules/cjs/loader.js:776:30)
    at Module.replacementCompile (/home/karl/dev/MOP/node_modules/append-transform/index.js:58:13)
    at Module._extensions..js (internal/modules/cjs/loader.js:787:10)
    at Object.<anonymous> (/home/karl/dev/MOP/node_modules/append-transform/index.js:62:4)
    at Module.load (internal/modules/cjs/loader.js:653:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
    at Function.Module._load (internal/modules/cjs/loader.js:585:3)

Which is being thrown by: import "zone.js/dist/mocha-patch";

HichamBI commented 5 years ago

Hi @basickarl,

Are you trying to execute your unit tests in an node environment with Jsdom ?

Mocha-patch work only when you are executing unit tests in the browser.

Please check this thread if you are looking for a working setup (Angular 7 and webpack 4) : http://hichambi.github.io/2016/12/27/testing-angular2-with-webpack-mocha-on-browser-and-node.html

BR.

angular-automatic-lock-bot[bot] commented 5 years ago

This issue has been automatically locked due to inactivity. Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.