我想在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
拜托,有人可以指出我正确的方向,给我一个例子,告诉我我做错了什么?非常感谢你。
答案 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请求: