QUnit은 jQuery 팀이 개발했으며 JavaScript 단위 테스트를 위한 탁월한 프레임워크입니다. 이 튜토리얼에서는 QUnit이 정확히 무엇인지, 왜 코드를 엄격하게 테스트해야 하는지 설명하겠습니다.
QUnit은 코드 디버깅에 도움이 되는 강력한 JavaScript 단위 테스트 프레임워크입니다. jQuery 팀 구성원이 작성했으며 jQuery의 공식 테스트 제품군입니다. 그러나 QUnit은 Rhino나 V8과 같은 특정 JavaScript 엔진을 통해 일반 JavaScript 코드는 물론 서버측 JavaScript까지 테스트할 수 있을 만큼 일반적입니다.
"단위 테스트" 개념에 익숙하지 않더라도 걱정하지 마세요. 이해하기 어렵지 않습니다:
컴퓨터 프로그래밍에서 단위 테스트는 프로그래머가 소스 코드의 개별 단위를 사용 적합성을 테스트하는 소프트웨어 검증 및 검증 방법입니다. 단위는 애플리케이션에서 테스트할 수 있는 가장 작은 부분입니다. 절차적 프로그래밍에서 단위는 개별 함수나 절차일 수 있습니다.
이 내용은 위키피디아에서 인용되었습니다. 간단히 말해서, 코드의 모든 기능에 대한 테스트를 작성하고 이러한 테스트가 모두 통과되면 코드에 버그가 없음을 확신할 수 있습니다(주로 테스트가 얼마나 철저한지에 따라 다름).
이전에 단위 테스트를 작성해 본 적이 없다면 코드를 웹 사이트에 직접 적용하고 잠시 동안 클릭하여 문제가 발생하는지 확인하고 문제가 발견되면 수정해 보세요. 이 접근 방식에는 많은 문제가 있습니다.
우선 매우 지루합니다. 클릭하는 것은 실제로 쉬운 작업이 아닙니다. 모든 것이 클릭되었는지 확인해야 하고 한두 가지를 놓칠 가능성이 있기 때문입니다. 둘째, 테스트를 위해 수행한 작업은 재사용이 불가능하므로 회귀 분석이 쉽지 않습니다. 회귀란 무엇입니까? 코드를 작성하고, 테스트하고, 발견한 버그를 수정한 다음 출시했다고 상상해 보세요. 그런 다음 사용자는 새로운 버그에 대한 피드백을 보내고 몇 가지 새로운 기능을 요청합니다. 코드로 돌아가서 새로운 버그를 수정하고 새로운 기능을 추가하세요. 다음에 일어날 수 있는 일은 오래된 버그가 다시 나타나는 것인데, 이것을 "회귀"라고 합니다. 이제 다시 클릭해야 하므로 오래된 버그를 더 이상 발견하지 못할 가능성이 높습니다. 발견하더라도 문제가 회귀로 인해 발생했다는 사실을 알기까지는 시간이 좀 걸릴 것입니다. 단위 테스트를 사용하면 버그를 찾기 위한 테스트를 작성할 수 있으며, 코드가 수정되면 테스트를 통해 다시 필터링할 수 있습니다. 회귀가 발생하면 일부 테스트는 확실히 실패하며 코드의 어느 부분에 오류가 있는지 알면 쉽게 발견할 수 있습니다. 방금 수정한 내용을 알고 있으므로 쉽게 수정할 수 있습니다.
단위 테스트의 또 다른 장점은 특히 웹 개발에 적용됩니다. 즉, 브라우저 간 호환성 테스트를 단순화합니다. 다른 브라우저에서 테스트를 실행하고, 한 브라우저에 문제가 있는 경우 이를 수정하고 테스트를 다시 실행하여 다른 브라우저에서 회귀가 발생하지 않도록 할 수 있습니다. 모든 대상 브라우저가 테스트를 통과하면 해당 브라우저가 모두 지원되는지 확인할 수 있습니다.
John Resig의 프로젝트 중 하나인 TestSwarm을 언급하고 싶습니다. 배포함으로써 JavaScript 단위 테스트를 다음 단계로 끌어올립니다. 이것은 많은 테스트가 있는 웹사이트입니다. 누구나 거기에 가서 몇 가지 테스트를 실행한 다음 결과를 서버에 반환할 수 있습니다. 이렇게 하면 다양한 브라우저와 플랫폼에서도 코드를 매우 빠르게 테스트할 수 있습니다.
그렇다면 QUnit을 사용하여 단위 테스트를 작성하는 방법은 무엇일까요? 먼저 테스트 환경을 설정해야 합니다.
으아아아보시다시피 여기서는 QUnit 프레임워크의 관리 버전이 사용됩니다.
테스트하려는 코드는 myProject.js에 들어가야 하고, 테스트는 myTests.js에 들어가야 합니다. 이러한 테스트를 실행하려면 브라우저에서 이 HTML 파일을 열면 됩니다. 이제 몇 가지 테스트를 작성할 차례입니다.
단위 테스트의 구성 요소는 어설션입니다.
어설션은 코드가 반환할 내용을 예측하는 문입니다. 예측이 틀리면 어설션이 실패하고 뭔가 잘못되었음을 알 수 있습니다.
어설션을 실행하려면 테스트 케이스에 넣어야 합니다.
으아아아여기서 숫자가 짝수인지 확인하는 isEven 함수를 정의했으며, 이 함수가 잘못된 답을 반환하지 않는지 확인하기 위해 이 함수를 테스트하고 싶습니다.
먼저 테스트 사례를 구성하는 test()를 호출합니다. 첫 번째 매개변수는 결과에 표시될 문자열이고 두 번째 매개변수는 우리의 주장을 포함하는 콜백 함수입니다. 이 콜백 함수는 QUnit이 실행되면 호출됩니다.
우리는 5개의 어설션을 작성했는데 모두 부울입니다. 부울 어설션은 첫 번째 인수가 참일 것으로 기대합니다. 두 번째 매개변수도 결과에 표시되는 메시지입니다.
테스트를 실행하면 다음과 같은 결과를 얻을 수 있습니다.
由于所有这些断言都已成功通过,我们可以非常确定 isEven() 将按预期工作。
让我们看看如果断言失败会发生什么。
// Let's test this function function isEven(val) { return val % 2 === 0; } test('isEven()', function() { ok(isEven(0), 'Zero is an even number'); ok(isEven(2), 'So is two'); ok(isEven(-4), 'So is negative four'); ok(!isEven(1), 'One is not an even number'); ok(!isEven(-7), 'Neither does negative seven'); // Fails ok(isEven(3), 'Three is an even number'); })
结果如下:
断言失败是因为我们故意写错了,但在你自己的项目中,如果测试没有通过,并且所有断言都是正确的,你就知道发现了一个bug。
ok() 并不是 QUnit 提供的唯一断言。在测试项目时,还有其他类型的断言很有用:
比较断言 equals() 期望其第一个参数(即实际值)等于其第二个参数(即期望值)。它与 ok() 类似,但同时输出实际值和期望值,使调试更加容易。与 ok() 一样,它采用可选的第三个参数作为要显示的消息。
所以代替:
test('assertions', function() { ok( 1 == 1, 'one equals one'); })
你应该写:
test('assertions', function() { equals( 1, 1, 'one equals one'); })
注意最后一个“1”,这是比较值。
如果值不相等:
test('assertions', function() { equals( 2, 1, 'one equals one'); })
它提供了更多信息,使生活变得更加轻松。
比较断言使用“==”来比较其参数,因此它不处理数组或对象比较:
test('test', function() { equals( {}, {}, 'fails, these are different objects'); equals( {a: 1}, {a: 1} , 'fails'); equals( [], [], 'fails, there are different arrays'); equals( [1], [1], 'fails'); })
为了测试这种相等性,QUnit 提供了另一种断言:相同断言。
相同的断言,same(),需要与 equals() 相同的参数,但它是一个深度递归比较断言,不仅适用于基本类型,还适用于数组和对象。在前面的示例中,如果将断言更改为相同的断言,它们将全部通过:
test('test', function() { same( {}, {}, 'passes, objects have the same content'); same( {a: 1}, {a: 1} , 'passes'); same( [], [], 'passes, arrays have the same content'); same( [1], [1], 'passes'); })
请注意,same() 在可能的情况下使用“===”进行比较,因此在比较特殊值时它会派上用场:
test('test', function() { equals( 0, false, 'true'); same( 0, false, 'false'); equals( null, undefined, 'true'); same( null, undefined, 'false'); })
将所有断言放在一个测试用例中是一个非常糟糕的主意,因为它很难维护,并且不会返回干净的结果。您应该做的是构建它们,将它们放入不同的测试用例中,每个测试用例都针对单一功能。
您甚至可以通过调用模块函数将测试用例组织到不同的模块中:
module('Module A'); test('a test', function() {}); test('an another test', function() {}); module('Module B'); test('a test', function() {}); test('an another test', function() {});
在前面的示例中,所有断言都是同步调用的,这意味着它们依次运行。在现实世界中,还有很多异步函数,例如ajax调用或setTimeout()和setInterval()调用的函数。我们如何测试这些类型的功能? QUnit 提供了一种特殊的测试用例,称为“异步测试”,专门用于异步测试:
我们先尝试用常规的方式来写:
test('asynchronous test', function() { setTimeout(function() { ok(true); }, 100) })
看到了吗?就好像我们没有写任何断言一样。这是因为断言是异步运行的,当它被调用时,测试用例已经完成。
这是正确的版本:
test('asynchronous test', function() { // Pause the test first stop(); setTimeout(function() { ok(true); // After the assertion has been called, // continue the test start(); }, 100) })
在这里,我们使用 stop() 暂停测试用例,调用断言后,我们使用 start() 继续。
调用 test() 后立即调用 stop() 是很常见的;所以QUnit提供了一个快捷方式:asyncTest()。您可以像这样重写前面的示例:
asyncTest('asynchronous test', function() { // The test is automatically paused setTimeout(function() { ok(true); // After the assertion has been called, // continue the test start(); }, 100) })
有一点需要注意:setTimeout() 将始终调用其回调函数,但如果它是自定义函数(例如 ajax 调用)怎么办?您如何确定回调函数将被调用?如果不调用回调,则不会调用 start(),整个单元测试将挂起:
所以这就是你要做的:
// A custom function function ajax(successCallback) { $.ajax({ url: 'server.php', success: successCallback }); } test('asynchronous test', function() { // Pause the test, and fail it if start() isn't called after one second stop(1000); ajax(function() { // ...asynchronous assertions start(); }) })
您将超时传递给 stop(),它告诉 QUnit,“如果在该超时后未调用 start(),则该测试应该失败。”您可以确信整个测试不会挂起,并且如果出现问题您将会收到通知。
多个异步函数怎么样?你把start()放在哪里?你把它放在setTimeout()中:
// A custom function function ajax(successCallback) { $.ajax({ url: 'server.php', success: successCallback }); } test('asynchronous test', function() { // Pause the test stop(); ajax(function() { // ...asynchronous assertions }) ajax(function() { // ...asynchronous assertions }) setTimeout(function() { start(); }, 2000); })
超时应该足够长,以允许在测试继续之前调用两个回调。但是如果其中一个回调没有被调用怎么办?你怎么知道这一点?这就是expect() 发挥作用的地方:
// A custom function function ajax(successCallback) { $.ajax({ url: 'server.php', success: successCallback }); } test('asynchronous test', function() { // Pause the test stop(); // Tell QUnit that you expect three assertions to run expect(3); ajax(function() { ok(true); }) ajax(function() { ok(true); ok(true); }) setTimeout(function() { start(); }, 2000); })
你向expect()传递一个数字来告诉QUnit你期望运行X个断言,如果其中一个断言没有被调用,数字将不匹配,并且你会被通知有事情发生错了。
expect() 还有一个快捷方式:只需将数字作为第二个参数传递给 test() 或 asyncTest():
// A custom function function ajax(successCallback) { $.ajax({ url: 'server.php', success: successCallback }); } // Tell QUnit that you expect three assertion to run test('asynchronous test', 3, function() { // Pause the test stop(); ajax(function() { ok(true); }) ajax(function() { ok(true); ok(true); }) setTimeout(function() { start(); }, 2000); })
这就是开始使用 QUnit 所需了解的全部内容。单元测试是在发布代码之前测试代码的好方法。如果您之前没有编写过任何单元测试,那么现在就开始吧!感谢您的阅读!
위 내용은 QUnit으로 JavaScript 코드 테스트하기: 단계별 가이드의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!