PhantomJS QUnitTeamCityDriver QUnit测试运行

时间:2012-10-19 20:16:18

标签: javascript unit-testing qunit phantomjs

我想在TeamCity中使用JavaScript的单元测试。

我正在使用QUnit,我看过几个建议使用phantomjs和QUnitTeamCityDriver的地方。我只是无法让它工作......

我在这方面没有太多的经验,似乎甚至无法让phantomjs在命令行中运行单元测试。

我确实从QUnitTeamCityDriver:simple_test.html

复制了这个例子
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>QUnit Example</title>
  <link rel="stylesheet" href="test_tools/qunit-1.10.0.css">
  <script type="text/javascript" src="resources/jquery-1.8.1.js"></script>
  <script type="text/javascript" src="test_tools/qunit-1.10.0.js"></script>
  <script type="text/javascript" src="qunit_teamcity_driver/QUnitTeamCityDriver.js"></script> 
  <script type="text/javascript" src="tests.js"></script> 
</head>
<body>
  <div id="qunit"></div>
  <h1 id="qunit-header">QUnit example</h1>
  <h2 id="qunit-banner"></h2>
  <div id="qunit-testrunner-toolbar"></div>
  <h2 id="qunit-userAgent"></h2>
  <ol id="qunit-tests"></ol>
  <div id="qunit-fixture">test markup, will be hidden</div>   
</body>
</html>
如果我使用simple_test.html尝试,那么

tests.js有一些简单的测试是否有效。

当然,html中的其他引用文件位于相应的文件夹中。

phantomjs.exe,tests.js和simple_test.html位于我调用的目录的根目录中。

TeamCity构建的指示是:<​​/ p>

Add a "Command Line" Build Step to your build in TeamCity which executes Tests.htm via PhantomJS
Command executable: C:\PhamtomJS\phantomjs.exe
Command parameters: \Scripts\QUnitTeamCityDriver.phantom.js Tests.htm

(这不起作用,所以我想在插入TeamCity内的命令行之前测试实际的命令行)

我尝试过的一些事情:

phantomjs.exe tests.js
phantomjs.exe tests.js simple_test.html
phantomjs.exe simple_test.html
phantomjs.exe test_tools\qunit-1.10.0.js tests.js simple_test.html
phantomjs.exe qunit_teamcity_driver/QUnitTeamCityDriver.phantom.js simple_test.html

结果:无论是解析错误还是找不到变量:test

拜托,有人可以指出我正确的方向,给我一个例子,告诉我我做错了什么?非常感谢你。

3 个答案:

答案 0 :(得分:6)

如果您的技术堆栈匹配,我已成功使用Chutzpah

除此之外,它通过处理来自kabaros描述的phantomjs的调用以及提供TeamCity和Visual Studio集成来为您做繁重的工作。

答案 1 :(得分:2)

简短的回答是,你需要运行:

phantomjs.exe script-runner.js simple_test.html

答案很长:

你需要一个可以调用你的测试的javascript文件,我写了很多变种,但我发现的最好的是QUnitTeamCityDriver https://github.com/redbadger/QUnitTeamCityDriver。即使我不与TeamCity合作,我也没有多少变化。

这个文件的内容可能有些变化(我在最近的一个项目中使用过它),但是你应该在github上找到原来的那个(把它称为script-runner.js并放入文件夹为phantom.exe以方便):

function waitFor(testFx, onReady, timeOutMillis) {
    var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3001, //< Default Max Timout is 3s
        start = new Date().getTime(),
        condition = false,
        interval = setInterval(function () {
            if ((new Date().getTime() - start < maxtimeOutMillis) && !condition) {
                // If not time-out yet and condition not yet fulfilled
                condition = (typeof (testFx) === "string" ? eval(testFx) : testFx()); //< defensive code
            } else {
                if (!condition) {
                    // If condition still not fulfilled (timeout but condition is 'false')
                    console.log("'waitFor()' timeout");
                    phantom.exit(1);
                } else {
                    // Condition fulfilled (timeout and/or condition is 'true')
                    typeof (onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled
                    clearInterval(interval); //< Stop this interval
                }
            }
        }, 100); //< repeat check every 250ms
};

var page = new WebPage();

// Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this")
page.onConsoleMessage = function (msg) {
    console.log(msg);
};

page.open(phantom.args[0], function (status) {

    if (status !== "success") {
        console.log("Unable to access network");
        phantom.exit(1);
    } else {
        waitFor(function () {
            return page.evaluate(function () {
                var el = document.getElementById('qunit-testresult');
                if (el && el.innerText.match('completed')) {
                    return true;
                }
                return false;
            });
        }, function () {
            phantom.exit();
        }, 10000);
    }
});

这将基本打开你的html页面并解析它。这对我来说是一个令人困惑的部分,phantomjs最后是一个浏览器,你应该传递这个脚本,最终将打开一个HTML页面而不是Javascript文件。所以你这样称呼phantomjs:

phantomjs.exe script-runner.js [path to file containing tests]

所以在你的情况下它将是:

phantomjs.exe script-runner.js simple_test.html

最后一点是,在包含测试的html页面中,您应该添加对javascript文件的引用,它是您的引用中名为QUnitTeamCityDriver.js的文件。这将连接到qUnit事件,并将它们引导到幻像,决定什么是通过测试,什么是失败的测试。文件的内容(再次,您可以在github的项目页面上找到更好的最新版本):

/*
Reference this file in the html files of the QUnit tests
Based on the work on this team city driver: https://github.com/redbadger/QUnitTeamCityDriver/
*/
if (navigator.userAgent.indexOf("PhantomJS") !== -1) {
    String.prototype.format = function () {
        var args = arguments;
        return this.replace(/{(\d+)}/g, function (match, number) {
            return typeof args[number] != 'undefined'
      ? args[number]
      : '{' + number + '}';
        });
    };

    var suiteName = "QUnit Tests";
    var currentTestName = "";
    var hasBegun = false;

    qunitBegin = function () {
        console.log("[testSuiteStarted name='{0}']".format(suiteName));
    };

    /* QUnit.testStart({ name }) */
    QUnit.testStart = function (args) {
        if (!hasBegun) {
            qunitBegin();
            hasBegun = true;
        }
        currentTestName = args.name;
    };

    QUnit.moduleStart = function (args) {
        console.log("Module started: {0}".format(args.name));
    };

    /* QUnit.log({ result, actual, expected, message }) */
    QUnit.log = function (args) {

        var currentAssertion = "{0} > {1}".format(currentTestName, args.message);

        //console.log("[testStarted name='{0}']".format(currentAssertion));

        if (!args.result) {
            console.log("**[testFailed] type='comparisonFailure' name='{0}' details='expected={1}, actual={2}' expected='{1}' actual='{2}'".format(currentAssertion, args.expected, args.actual));
        }

        console.log("[testFinished] name='{0}'".format(currentAssertion));
    };

    /* QUnit.done({ failed, passed, total, runtime }) */
    QUnit.done = function (args) {
        console.log("[testSuiteFinished name='{0}']".format(suiteName));
    };
}

这基本上挂钩了由QUnit定义的回调,并将消息发送到控制台,由phantomjs显示。可在此处找到QUnit回调文档:http://api.qunitjs.com/category/callbacks/

至于与TeamCity挂钩,它可能只是Build步骤定义中的一个设置,用于检查输出中的某个字符串(例如,输出中存在“[testFailed]”表示失败),我是用CruiseControl完成它并在它周围创建了一个Nunit包装器。最后,同样的概念适用,您只需解析phatomjs输出以检查是否存在[testFailed]或任何表示失败的字符串。

答案 2 :(得分:2)

除了上面提供的建议外,我建议使用qunit提供的新的phantomjs qunit runner:

https://github.com/jquery/qunit/tree/master/addons/phantomjs

如果您有错误的异步测试,请查看我的pull请求:

https://github.com/jquery/qunit/pull/415