scruffyDog
scruffyDog

Reputation: 831

qunit tests alternately fail

I have two tests, test1 and test2. When I run the tests either test1 will succeed and test2 will fail or test2 will succeed whilst test1 will fail. Both tests involve clicking a button which updates the DOM via some jquery methods. It appears that only the test which is run first will trigger these methods, does anyone have any idea why that is?

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Javascript tests</title>
    <link rel="stylesheet" href="http://code.jquery.com/qunit.css">
  </head>

  <body>
    <div id="qunit"></div>
    <div id="qunit-fixture">
      <div id="div1">
        <button class="selected">Property1</button>
        <button >Property2</button>
      </div> <!-- div1 -->

      <div id="div2">
        <table>
          <tbody>
            <tr>
              <th>Name</th>
              <td></td>
            </tr>
          <tbody>
        </table>
      </div> <!-- div2 -->

      <script src="http://code.jquery.com/jquery.min.js"></script>
      <script>
        $("#div1 button").click(function(){
          console.log("button clicked");
          $(this).addClass("selected"); // clicked button   is set to selected
          var nameElement = $("#div2 table td");
          nameElement.text($(this).text()); // updates the nameElement to   the text contents of the button clicked
          var buttons = $(this).parent().find("button");
          for(i=0; i<buttons.length; i++){
            if(buttons[i] != $(this)[0]){
              $(buttons[i]).removeClass("selected") // other buttons are set to unselected
            }
          }
        });
      </script>
    </div> <!-- qunit-fixture -->

    <script src="http://code.jquery.com/qunit/qunit-1.19.0.js"></script>

    <script>
      test("test clicking on a property button adds the selected class to it", function(){
      console.log("test1");

      var buttons = $("#div1 button");

      $(buttons[1]).click();
      equal($(buttons[0]).hasClass("selected"), false);
      equal($(buttons[1]).hasClass("selected"), true);

      $(buttons[0]).click();
      equal($(buttons[0]).hasClass("selected"), true);
      equal($(buttons[1]).hasClass("selected"), false);

      console.log("end of test1");
      });

      test("test clicking on a property button updates the property details", function(){

      console.log("test2");

      var buttons = $("#div1 button");
      var name = $("#div2 table td");

      buttons[1].click()
      equal(name.text(), "Property2")

      console.log("end of test2");
      });

    </script>

  </body>
</html>

The output from the console shows that the button click is only registered in the first test that is run. e.g.

test.html:49 test1
test.html:31 button clicked
test.html:31 button clicked
test.html:61 end of test1
test.html:66 test2
test.html:74 end of test2

Upvotes: 0

Views: 78

Answers (1)

Jeroen
Jeroen

Reputation: 63699

QUnit will instantiate the qunit-fixture and reset it before each test, but it will only reset the DOM parts of that element. You've placed your entire app / script under test inside that fixture, but that isn't reloaded once the second test is run.

Instead, try to move your app code to a seperate file or at least a seperate script block, and rewrite it slightly so it can be bootstrapped to a certain container, e.g.:

function myApp(container) {
    $(container).find("#div1 button").click(function(){
      console.log("button clicked");
      $(this).addClass("selected"); // clicked button   is set to selected
      var nameElement = $(container).find("#div2 table td");
      nameElement.text($(this).text()); // updates the nameElement to   the text contents of the button clicked
      var buttons = $(this).parent().find("button");
      for(i=0; i<buttons.length; i++){
        if(buttons[i] != $(this)[0]){
          $(buttons[i]).removeClass("selected") // other buttons are set to unselected
        }
      }
    });
}

In your actual app you'd have to fire that function, and in your tests you make sure it gets fired before every test so you start clean. E.g.:

QUnit.module("MyApp", {
    beforeEach: function () {
        myApp($("#qunit-fixture"));
    }
});

With this all your tests are cleanly seperated and will not re-use eachothers DOM.


Here's a full running example:

function myApp(container) {
  $(container).find("#div1 button").click(function(){
    console.log("button clicked");
    $(this).addClass("selected"); // clicked button   is set to selected
    var nameElement = $(container).find("#div2 table td");
    nameElement.text($(this).text()); // updates the nameElement to   the text contents of the button clicked
    var buttons = $(this).parent().find("button");
    for(i=0; i<buttons.length; i++){
      if(buttons[i] != $(this)[0]){
        $(buttons[i]).removeClass("selected") // other buttons are set to unselected
      }
    }
  });
}

QUnit.module("MyApp", {
  beforeEach: function () {
    myApp($("#qunit-fixture"));
  }
});

test("test clicking on a property button adds the selected class to it", function(){
  console.log("test1");

  var buttons = $("#div1 button");

  $(buttons[1]).click();
  equal($(buttons[0]).hasClass("selected"), false);
  equal($(buttons[1]).hasClass("selected"), true);

  $(buttons[0]).click();
  equal($(buttons[0]).hasClass("selected"), true);
  equal($(buttons[1]).hasClass("selected"), false);

  console.log("end of test1");
});

test("test clicking on a property button updates the property details", function(){

  console.log("test2");

  var buttons = $("#div1 button");
  var name = $("#div2 table td");

  buttons[1].click()
  equal(name.text(), "Property2")

  console.log("end of test2");
});
<link href="https://code.jquery.com/qunit/qunit-1.19.0.css" rel="stylesheet"/>
<script src="http://code.jquery.com/jquery.min.js"></script>
<script src="http://code.jquery.com/qunit/qunit-1.19.0.js"></script>

<div id="qunit"></div>
<div id="qunit-fixture">
  <div id="div1">
    <button class="selected">Property1</button>
    <button >Property2</button>
  </div> <!-- div1 -->
  <div id="div2">
    <table>
      <tbody>
        <tr>
          <th>Name</th>
          <td></td>
        </tr>
      <tbody>
    </table>
  </div> <!-- div2 -->
</div> <!-- qunit-fixture -->

Upvotes: 1

Related Questions