6

我注意到当测试后期发生异常时,qUnit 没有发出任何通知。例如,在 test() 中运行它:

stop();
function myfun(ed) {
    console.log('resumed');
    start(); //Resume qunit
    ok(1,'entered qunit again');
    ok(ed.getContent()== 'expected content') // < causes exception, no getContent() yet.
}
R.tinymce.onAddEditor.add(myfun)

在页面的内部 iframe 中会导致异常(TypeError: ed.getContent is not a function),但 Qunit 状态区域中没有任何内容说明这一点。我看到 0 次失败。

(R 是内部 iframe,在这里使用技术:http: //www.mattevanoff.com/2011/01/unit-testing-jquery-w-qunit/)假设这不是最好的方法,我是否正确去测试导致某些结果的 UI 交互序列?即使对于一些主要面向 JavaScript 的前端 Web 应用程序测试,使用 selenium 之类的东西总是更好吗?

附带说明一下,Firefox 控制台在此处的异常下方显示了 console.log,即使它首先发生...为什么?

4

1 回答 1

3

If you look into qUnit source code, there are two mechanisms handling exceptions. One is controlled by config.notrycatch setting and will wrap test setup, execution and teardown in try..catch blocks. This approach won't help much with exceptions thrown by asynchronous tests however, qUnit isn't the caller there. This is why there is an additional window.onerror handler controlled by Test.ignoreGlobalErrors setting. Both settings are false by default so that both kinds of exceptions are caught. In fact, the following code (essentially same as yours but without TinyMCE-specific parts) produces the expected results for me:

test("foo", function()
{
  stop();
  function myfun(ed)
  {
    start();
    ok(1, 'entered qunit again');
    throw "bar";
  }
  setTimeout(myfun, 1000);
});

I first see a passed tests with the message "entered qunit again" and then a failed one with the message: "uncaught exception: bar." As to why this doesn't work for you, I can see the following options:

  1. Your qUnit copy is more than two years old, before qUnit issue 134 was fixed and a global exception handler added.
  2. Your code is changing Test.ignoreGlobalErrors setting (unlikely).
  3. There is an existing window.onerror handler that returns true and thus tells qUnit that the error has been handled. I checked whether TinyMCE adds one by default but it doesn't look like it does.
  4. TinyMCE catches errors in event handlers when calling them. This is the logical thing to do when dealing with multiple callbacks, the usual approach is something like this:
for (var i = 0; i < callbacks.length; i++)
{
  try
  {
    callbacks[i]();
  }
  catch (e)
  {
    console.error(e);
  }
}

By redirecting all exceptions to console.error this makes sure that exceptions are still reported while all callbacks will be called even if one of them throws an exception. However, since the exception is handled jQuery can no longer catch it. Again, I checked whether TinyMCE implements this pattern - it doesn't look like it.

Update: Turns out there is a fifth option that I didn't think of: the exception is fired inside a frame and qUnit didn't set up its global error handler there (already because tracking frame creation is non-trivial, a new frame can be created any time). This should be easily fixed by adding the following code to the frame:

window.onerror = function()
{
  if (parent.onerror)
  {
    // Forward the call to the parent frame
    return parent.onerror.apply(parent, arguments);
  }
  else
    return false;
}

Concerning your side-note: the console object doesn't guarantee you any specific order in which messages appear. In fact, the code console.log("foo");throw "bar"; also shows the exception first, followed by the log message. This indicates that log messages are queued and handled delayed, probably for performance reasons. But you would need to look into the implementation of the console object in Firefox to be certain - this is an implementation detail.

于 2013-12-29T08:04:06.367 回答