Test user operation problems
Code dependent on user operations cannot be tested by executing functions. The events of elements usually use Asynchronous functions, such as click, which need to be simulated.
Solution
You can use jquery'sTrigger () method to trigger the event and then test the expected behavior. If you do not want the browser event to be triggered, you can use triggerhandler () to execute the Event-related method. This is helpful for the Click Event of the test link, because trigger () may cause the browser to change the address bar information, which may not happen during the test. Suppose we have a simple
Keylogger needs to be tested:
function KeyLogger( target ) { if ( !(this instanceof KeyLogger) ) { return new KeyLogger( target ); } this.target = target; this.log = []; var self = this; this.target.off( "keydown" ).on( "keydown", function( event ) { self.log.push( event.keyCode ); });}
We can manually trigger the keypress event and then observe whether the logger is working:
test( "keylogger api behavior", function() { var event, $doc = $( document ), keys = KeyLogger( $doc ); // trigger event event = $.Event( "keydown" ); event.keyCode = 9; $doc.trigger( event ); // verify expected behavior equal( keys.log.length, 1, "a key was logged" ); equal( keys.log[ 0 ], 9, "correct key was logged" ); });
Discussion
If your event processing does not depend on any specific event attribute, you can call trigger (eventtype ). However, if your event processing depends on special event attributes, you need to use $. event to create an event object and set necessary attributes. It is very important to trigger events related to complex behaviors, such as dragging, which consists of mousedown, mousemove, and mouseup. Even simple events may consist of many events. For example, click is composed of mousedown, mouseup, and click. Whether you need to trigger all three events depends on your test code and trigger only click. In most cases, the requirements are met.
If those are not enough, you need a framework to simulate user events:
- SYN "is a synthesis of event class libraries, used to handle most of the typing, clicking, moving and dragging, can accurately simulate the user's actual operations ". Qunit-based funcunit uses SYN to test the functions of web sites.
- Jsrobot-"a web application testing tool that can generate real percussion keyboards instead of simply simulating JavaScript event triggering. Allows you to trigger the actual events of the browser by hitting, which cannot be done by other frameworks ".
- DOH robot "provides an API that allows testers to automatically run the UI test using real, cross-platform, and system-level input events ". It provides events close to real browsers for you, but it must be implemented using Java applets.
Test atomicity
When the test is centralized, the test that should have passed will fail, and the test that should have failed will pass, because the side effects of the previous test will invalidate the test result.
test( "2 asserts", function() { var $fixture = $( "#qunit-fixture" ); $fixture.append( "<div>hello!</div>" ); equal( $( "div", $fixture ).length, 1, "div added successfully!" ); $fixture.append( "<span>hello!</span>" ); equal( $( "span", $fixture ).length, 1, "span added successfully!" );});
The first append () adds a DIV, and the second Equal () does not take it into consideration.
Solution
Use the test () method to maintain the atomicity of the test, so that each asserted is clean without any side effects. You should only rely on# Qunit-Fixture
The fixture label, modification and dependency on other things will have side effects.
test( "Appends a div", function() { var $fixture = $( "#qunit-fixture" ); $fixture.append( "<div>hello!</div>" ); equal( $( "div", $fixture ).length, 1, "div added successfully!" );}); test( "Appends a span", function() { var $fixture = $( "#qunit-fixture" ); $fixture.append("<span>hello!</span>" ); equal( $( "span", $fixture ).length, 1, "span added successfully!" );});
Qunit is reset after each test# Remove an existing event from the element in qunit-fixture. If you only use the internal elements of fixture, you do not need to perform manual operations to keep it atomic after each test.
Apart from
# Qunit-fixture and filtering ("
In addition, qunit also provides the noglobals mark. Let's take a look at the test below:
test( "global pollution", function() { window.pollute = true; ok( pollute, "nasty pollution" );});
In general, the test will get a successful result. However, after noglobals is selected, OK () returns the failed result because qunit considers that it has contaminated the window object. There is no need to use that flag at any time, but when integrating third-party class libraries, it is helpful to determine whether it will cause global naming pollution. In addition, he can also help find Bugs caused by side effects.
Group Test Problems
You have split all your tests to keep them atomic without side effects, but you want to ensure that their logic is organized and that they can run in groups.
Solution
You can use module () to group A test:
module( "group a" );test( "a basic test example", function() { ok( true, "this test is fine" );});test( "a basic test example 2", function() { ok( true, "this test is fine" );}); module( "group b" );test( "a basic test example 3", function() { ok( true, "this test is fine" );});test( "a basic test example 4", function() { ok( true, "this test is fine" );});
The tests after module () will be divided into one group, and the names of each test will be added with the module name in the test results. You can also run a specific test by selecting the module name.
Discussion
In addition to grouping, Module () can also be used to extract common code. It accepts an optional second parameter and defines the starting and ending behavior of each module running the test.
module( "module", { setup: function() { ok( true, "one extra assert per test" ); }, teardown: function() { ok( true, "and one extra assert after each test" ); }});test( "test with setup and teardown", function() { expect( 2 );});
You can define the setup and teardown attributes together, or define only one of them. When the modile () method is called again, the setup/teardown method defined in the previous method is reset.
Efficient Devlopment
When your test takes a long time (for example, several seconds), you do not need to waste time waiting for the results.
Solution
Qunit has a series of methods to solve this problem. The most interesting one is to click the "Hide passed tests" option in the header, so that qunit will only display the failed test, which will not affect the test time, but only focus on the failed test.
Another interesting feature is that qunit stores the name of the failed test in sessionstorage (your browser must support it). By default, this feature is enabled, but we didn't notice his existence. The next time you run the test again, the previous failed test will be executed first, but it will not affect the sequence of output results, only the execution order will be affected. With "Hide passed tests", you can immediately get a failed test.
Discussion
Automatic Sorting will happen by default. You think that your test requires atomicity. If it cannot be guaranteed, a random exception will occur. Fixing the problem is a correct solution or is difficult. You can set qunit. config. reorder = false.
In addition to automatic sorting, there are also some manual options. You can click the "Rerun" link after the test to run a single test. It will add the "testnumber = N" string parameter to the URL, and N represents the test number you clicked. You can refresh the page, run only that test, or run all tests in the return button area of the browser.
Run all tests in the module and work in almost the same way. Unless you select the module in the upper-right corner of the header, the "module = N" string will be added to the URL, and N represents the module number, for example :"? Module = testenvironment % 20 with % 20 object ".
Source: http://qunitjs.com/cookbook/