我正在尝试将一些 JavaScript 单元测试合并到我的自动化构建过程中。目前 JSUnit 与 JUnit 配合得很好,但它似乎是废弃软件,缺乏对 Ajax、调试和超时的良好支持。
有没有人有幸(使用Ant)自动化单元测试库,例如YUI测试、jQuery 的QUnit或jQUnit?
注意:我使用自定义构建的 Ajax 库,因此 Dojo 的 DOH 的问题在于它要求您使用自己的 Ajax 函数调用和事件处理程序来处理任何 Ajax 单元测试。
那里有许多 JavaScript 单元测试框架(JSUnit、scriptaculous 等),但 JSUnit 是我所知道的唯一一个可以用于自动构建的框架。
如果您正在进行“真正的”单元测试,则不需要 AJAX 支持。例如,如果您使用的是 DWR 等RPC Ajax 框架,您可以轻松编写一个模拟函数:
函数模拟函数(someArg,回调){ 变量结果 = ...; //一些处理 设置超时(
函数(){回调(结果);}, 300 // 一些虚假的延迟 ); }
是的,JSUnit 确实处理超时:Simulating Time in JSUnit Tests
我是js-test-driver的忠实粉丝。
它在CI环境中运行良好,并且能够捕获实际浏览器以进行跨浏览器测试。
我最近阅读了 Bruno 的一篇文章,使用 JSUnit 并在此基础上创建了一个 JsMock 框架……非常有趣。我正在考虑使用他的工作来开始对我的 JavaScript 代码进行单元测试。
我刚刚让 Hudson CI 运行 JasmineBDD(无头),至少对于纯 JavaScript 单元测试。
(Hudson 通过 shell 运行 Java,运行 Envjs,运行 JasmineBDD。)
不过,我还没有让它与一个大图书馆很好地配合,比如原型。
查看YUITest
我同意 JSUnit 有点垂死挣扎。我们刚刚用 YUI 测试完成了替换它。
与使用 qUnit 的示例类似,我们使用Selenium运行测试。我们独立于我们的其他 Selenium 测试运行这个测试,仅仅是因为它没有正常的 UI 回归测试所具有的依赖关系(例如,将应用程序部署到服务器)。
首先,我们有一个包含在所有测试 HTML 文件中的基本 JavaScript 文件。这处理设置 YUI 实例、测试运行器、YUI.Test.Suite 对象以及 Test.Case。它具有可以通过 Selenium 访问以运行测试套件的方法,检查测试运行器是否仍在运行(在它完成之前结果不可用),并获取测试结果(我们选择 JSON 格式):
var yui_instance; // The YUI instance
var runner; // The YAHOO.Test.Runner
var Assert; // An instance of YAHOO.Test.Assert to save coding
var testSuite; // The YAHOO.Test.Suite that will get run.
/**
* Sets the required value for the name property on the given template, creates
* and returns a new YUI Test.Case object.
*
* @param template the template object containing all of the tests
*/
function setupTestCase(template) {
template.name = "jsTestCase";
var test_case = new yui_instance.Test.Case(template);
return test_case;
}
/**
* Sets up the test suite with a single test case using the given
* template.
*
* @param template the template object containing all of the tests
*/
function setupTestSuite(template) {
var test_case = setupTestCase(template);
testSuite = new yui_instance.Test.Suite("Bond JS Test Suite");
testSuite.add(test_case);
}
/**
* Runs the YAHOO.Test.Suite
*/
function runTestSuite() {
runner = yui_instance.Test.Runner;
Assert = yui_instance.Assert;
runner.clear();
runner.add(testSuite);
runner.run();
}
/**
* Used to see if the YAHOO.Test.Runner is still running. The
* test results are not available until it is done running.
*/
function isRunning() {
return runner.isRunning();
}
/**
* Gets the results from the YAHOO.Test.Runner
*/
function getTestResults() {
return runner.getResults(yui_instance.Test.Format.JSON);
}
至于 Selenium 方面,我们使用了参数化测试。我们在 Internet Explorer 和 Firefox 的 data 方法中运行我们的测试,将测试结果解析为一个 Object 数组列表,每个数组包含浏览器名称、测试文件名称、测试名称、结果(通过、失败或忽略) 和消息。
实际测试只是断言测试结果。如果它不等于“通过”,那么它会通过从 YUI 测试结果返回的消息使测试失败。
@Parameters
public static List<Object[]> data() throws Exception {
yui_test_codebase = "file:///c://myapppath/yui/tests";
List<Object[]> testResults = new ArrayList<Object[]>();
pageNames = new ArrayList<String>();
pageNames.add("yuiTest1.html");
pageNames.add("yuiTest2.html");
testResults.addAll(runJSTestsInBrowser(IE_NOPROXY));
testResults.addAll(runJSTestsInBrowser(FIREFOX));
return testResults;
}
/**
* Creates a Selenium instance for the given browser, and runs each
* YUI Test page.
*
* @param aBrowser
* @return
*/
private static List<Object[]> runJSTestsInBrowser(Browser aBrowser) {
String yui_test_codebase = "file:///c://myapppath/yui/tests/";
String browser_bot = "this.browserbot.getCurrentWindow()"
List<Object[]> testResults = new ArrayList<Object[]>();
selenium = new DefaultSelenium(APPLICATION_SERVER, REMOTE_CONTROL_PORT, aBrowser.getCommand(), yui_test_codebase);
try {
selenium.start();
/*
* Run the test here
*/
for (String page_name : pageNames) {
selenium.open(yui_test_codebase + page_name);
//Wait for the YAHOO instance to be available
selenium.waitForCondition(browser_bot + ".yui_instance != undefined", "10000");
selenium.getEval("dom=runYUITestSuite(" + browser_bot + ")");
// Output from the tests is not available until
// the YAHOO.Test.Runner is done running the suite
selenium.waitForCondition("!" + browser_bot + ".isRunning()", "10000");
String output = selenium.getEval("dom=getYUITestResults(" + browser_bot + ")");
JSONObject results = JSONObject.fromObject(output);
JSONObject test_case = results.getJSONObject("jsTestCase");
JSONArray testCasePropertyNames = test_case.names();
Iterator itr = testCasePropertyNames.iterator();
/*
* From the output, build an array with the following:
* Test file
* Test name
* status (result)
* message
*/
while(itr.hasNext()) {
String name = (String)itr.next();
if(name.startsWith("test")) {
JSONObject testResult = test_case.getJSONObject(name);
String test_name = testResult.getString("name");
String test_result = testResult.getString("result");
String test_message = testResult.getString("message");
Object[] testResultObject = {aBrowser.getCommand(), page_name, test_name, test_result, test_message};
testResults.add(testResultObject);
}
}
}
} finally {
// If an exception is thrown, this will guarantee that the selenium instance
// is shut down properly
selenium.stop();
selenium = null;
}
return testResults;
}
/**
* Inspects each test result and fails if the testResult was not "pass"
*/
@Test
public void inspectTestResults() {
if(!this.testResult.equalsIgnoreCase("pass")) {
fail(String.format(MESSAGE_FORMAT, this.browser, this.pageName, this.testName, this.message));
}
}
有一个新项目可让您在 Java 环境(如 Ant)中运行QUnit测试,因此您可以将客户端测试套件与其他单元测试完全集成。
http://qunit-test-runner.googlecode.com
我用它对 jQuery 插件、objx代码、自定义 OO JavaScript 进行了单元测试,它无需修改即可适用于所有内容。
我正在处理的项目使用Js-Test-Driver在 Chrome 10 上托管Jasmine和Jasmine-JSTD-Adapter,包括使用JS-Test-Driver 中包含的代码覆盖率测试。
虽然每次我们在CI 环境中更改或更新浏览器时都会出现一些问题,但 Jasmine 测试运行得非常顺利,只有异步测试的小问题,但据我所知,这些可以使用 Jasmine Clock 解决,但我还没有机会修补它们。
我发布了一个小库,用于验证依赖于浏览器的 JavaScript 测试,而无需使用浏览器。它是一个 Node.js 模块,它使用zombie.js 加载测试页面并检查结果。我已经在我的博客上写过它。这是自动化的样子:
var browsertest = require('../browsertest.js').browsertest;
describe('browser tests', function () {
it('should properly report the result of a mocha test page', function (done) {
browsertest({
url: "file:///home/liam/work/browser-js-testing/tests.html",
callback: function() {
done();
}
});
});
});
另一个可以与 Ant 一起运行的 JavaScript 测试框架是CrossCheck。在项目的构建文件中有一个通过 Ant 运行 CrossCheck 的示例。
CrossCheck 尝试模拟浏览器,但成功有限,包括XMLHttpRequest和超时/间隔的模拟样式实现。
不过,它目前不处理从网页加载 JavaScript。您必须指定要加载和测试的 JavaScript 文件。如果您将所有 JavaScript 代码与 HTML 分开,它可能对您有用。