In the current software development, unit testing has become more and more important. Compared with the manual testing of programmers and QA, unit testing can be integrated and run during each build of the project, to provide RegressionTest feedback for programmers. This is an important support for programmers in Agile development.
In the current software development, unit testing has become increasingly important. compared with manual testing by programmers and QA, unit testing can be integrated and run during each build of the project to provide Regression Test feedback to programmers. this provides very important support for programmers in Agile development and can be easily reconstructed. currently, mainstream programming languages have excellent support for unit testing, and many articles about JUnit and NUnit are available. here, we will introduce how to use QUnit to perform unit tests on Javascript scripts.
Here we use a simple poker example. First, we use Javascript to write a simple poker class:
var Card = function(opts) { var C = {}; C._normalizeArg = function(arg) { if (_.isString(arg)) { arg = arg.replace(/\s+/g,'').split(","); } if (!_.isArray(arg)) { arg = [arg]; } return arg; }; C.extend = function(obj) { _(C).extend(obj); return C; }; C.CardKind = { "Spade" : "spade", "Heart" : "heart", "Diamond" : "diamond", "Club" : "club", "Special" : "special" }; C.CardNum = { "Ace" : 1, "Two" : 2, "Three" : 3, "Four" : 4, "Five" : 5, "Six" : 6, "Seven" : 7, "Eight" : 8, "Nine" : 9, "Ten" : 10, "Jack" : 11, "Queen" : 12, "King" : 13, "JokerS" : 14, "Joker" : 15 }; C.Card = Class.extend({ init : function(kind, num) { this.kind = kind; this.num = num; }, name : function() { return this.kind + this.num; }, isComparable : function(card) { return this.kind == card.kind; }, compareTo : function (card) { if (this.isComparable(card)) { return this.num - card.num; } } }); C.Deck = Class.extend({ init : function(numOfDecks, includeJokers, jokersAreDifferent, cards) { this.numOfDecks = numOfDecks == undefined ? 1 : numOfDecks; this.includeJokers = includeJokers == undefined ? false : includeJokers; this.jokersAreDifferent = jokersAreDifferent == undefined ? false : jokersAreDifferent; this.cards = []; this.setup(cards); }, setup : function(cards) { if (cards == undefined) { var kinds = _.filter(C.CardKind, function(kind) {return kind != C.CardKind.Special; }); var nums = _.filter(C.CardNum, function(num) {return num <= C.CardNum.King; }); for (var i = 1; i <= this.numOfDecks; i++) { for (var kind in kinds) { for (var num in nums) { this.cards.push(new C.Card(kinds[kind], nums[num])); } } if (this.includeJokers) { if (this.jokersAreDifferent) { this.cards.push(new C.Card(C.CardKind.Special, C.CardNum.JokerS)); this.cards.push(new C.Card(C.CardKind.Special, C.CardNum.Joker)); } else { this.cards.push(new C.Card(C.CardKind.Special, C.CardNum.Joker)); this.cards.push(new C.Card(C.CardKind.Special, C.CardNum.Joker)); } } } } else { this.cards = cards; } var cardIndexes = new Array(); var currentIndex = -1; for (var i = 0; i < this.totalNumOfCards(); i++) { cardIndexes[i] = i; } this.currentCard = function() { return this.cards[cardIndexes[currentIndex]]; }; this.shuffle = function() { cardIndexes = _.shuffle(cardIndexes); currentIndex = -1; return this; }; this.availableNumOfCards = function() { return this.totalNumOfCards() - currentIndex - 1; }; this.getCard = function() { if (this.availableNumOfCards() > 0) { currentIndex++; return this.currentCard(); } }; this.skip = function(num) { if (this.availableNumOfCards() >= num) { currentIndex += num; } return this; } }, totalNumOfCards : function() { return this.cards.length; } }); return C;};
This simple class defines 54 cards for a Deck of cards and a Deck class. It provides the generation of a Deck card and some simple methods. Next we will add a unit test for these methods:
test('Card.init', function() { var C = Card(); var card = new C.Card(C.CardKind.Club, C.CardNum.Ace); QUnit.equal(card.name(), 'club1', 'card Club Ace has name club1'); var card = new C.Card(C.CardKind.Special, C.CardNum.Joker); QUnit.equal(card.name(), 'special15', 'card Special Joker has name special15');});test('Card.isComparable', function() { var C = Card(); var card1 = new C.Card(C.CardKind.Club, C.CardNum.Ace); var card2 = new C.Card(C.CardKind.Club, C.CardNum.Two); QUnit.equal(card1.isComparable(card2), true, 'Club Ace is comparable with Club Two'); var C = Card(); var card1 = new C.Card(C.CardKind.Club, C.CardNum.Ace); var card2 = new C.Card(C.CardKind.Heart, C.CardNum.Two); QUnit.equal(card1.isComparable(card2), false, 'Club Ace is not comparable with Heart Two');});test('Card.compareTo', function() { var C = Card(); var card1 = new C.Card(C.CardKind.Club, C.CardNum.Ace); var card2 = new C.Card(C.CardKind.Heart, C.CardNum.Two); QUnit.equal(card1.compareTo(card2) == undefined, true, 'Club Ace compares to Heart Two gets undefined'); var C = Card(); var card1 = new C.Card(C.CardKind.Club, C.CardNum.Ace); var card2 = new C.Card(C.CardKind.Club, C.CardNum.Two); QUnit.equal(card1.compareTo(card2) < 0, true, 'Club Ace is smaller to Club Two'); var C = Card(); var card1 = new C.Card(C.CardKind.Club, C.CardNum.Ace); var card2 = new C.Card(C.CardKind.Club, C.CardNum.Ace); QUnit.equal(card1.compareTo(card2) == 0, true, 'Club Ace equals to Club Ace');});test('Deck.init(numOfDecks : 1)', function() { var C = Card(); var deck = new C.Deck(); QUnit.equal(deck.totalNumOfCards(), 52, '1 deck contains 52 cards'); QUnit.equal(_.all(deck.cards, function(card) { return card.kind != C.CardKind.Special && card.num <= C.CardNum.King; }), true, 'There is no jokers'); var counts = _.countBy(deck.cards, function(card) { return card.kind; }); QUnit.equal(counts[C.CardKind.Club], 13, '13 club cards'); QUnit.equal(counts[C.CardKind.Diamond], 13, '13 diamond cards'); QUnit.equal(counts[C.CardKind.Heart], 13, '13 heart cards'); QUnit.equal(counts[C.CardKind.Spade], 13, '13 spade cards'); var counts2 = _.countBy(deck.cards, function(card) { return card.num; }); QUnit.equal(counts2[C.CardNum.Ace], 4, '4 Ace cards'); QUnit.equal(counts2[C.CardNum.Two], 4, '4 Two cards'); QUnit.equal(counts2[C.CardNum.Three], 4, '4 Three cards'); QUnit.equal(counts2[C.CardNum.Four], 4, '4 Four cards'); QUnit.equal(counts2[C.CardNum.Five], 4, '4 Five cards'); QUnit.equal(counts2[C.CardNum.Six], 4, '4 Six cards'); QUnit.equal(counts2[C.CardNum.Seven], 4, '4 Seven cards'); QUnit.equal(counts2[C.CardNum.Eight], 4, '4 Eight cards'); QUnit.equal(counts2[C.CardNum.Nine], 4, '4 Nine cards'); QUnit.equal(counts2[C.CardNum.Ten], 4, '4 Ten cards'); QUnit.equal(counts2[C.CardNum.Jack], 4, '4 Jack cards'); QUnit.equal(counts2[C.CardNum.Queen], 4, '4 Queen cards'); QUnit.equal(counts2[C.CardNum.King], 4, '4 King cards');});test('Deck.init with Jokers', function() { var C = Card(); var deck = new C.Deck(1, true, true); QUnit.equal(deck.totalNumOfCards(), 54, '1 deck contains 52 cards and 2 jokers'); var counts = _.countBy(deck.cards, function(card) { return card.kind; }); QUnit.equal(counts[C.CardKind.Special], 2, '2 jokers'); var counts2 = _.countBy(deck.cards, function(card) { return card.num; }); QUnit.equal(counts2[C.CardNum.JokerS], 1, '1 small joker'); QUnit.equal(counts2[C.CardNum.Joker], 1, '1 big joker'); var C = Card(); var deck = new C.Deck(1, true, false); QUnit.equal(deck.totalNumOfCards(), 54, '1 deck contains 52 cards and 2 jokers'); var counts = _.countBy(deck.cards, function(card) { return card.kind; }); QUnit.equal(counts[C.CardKind.Special], 2, '2 jokers'); var counts2 = _.countBy(deck.cards, function(card) { return card.num; }); QUnit.equal(counts2[C.CardNum.JokerS], undefined, 'there is no small joker'); QUnit.equal(counts2[C.CardNum.Joker], 2, '2 big jokers');});test('Deck.utilities', function() { var C = Card(); var deck = new C.Deck(); QUnit.equal(deck.currentCard() == undefined, true, "call current card without getting the first card gets no card"); QUnit.equal(deck.availableNumOfCards(), deck.totalNumOfCards(), "all cards are available"); QUnit.equal(deck.getCard() == deck.cards[0], true, "getCard gets the first card without shuffle"); QUnit.equal(deck.getCard().compareTo(deck.cards[1]), 0, "getCard call again gets the second card without shuffle using compareTo"); QUnit.equal(deck.shuffle() instanceof C.Deck, true, "shuffle function returns the deck back"); QUnit.equal(deck.availableNumOfCards(), deck.totalNumOfCards(), "after shuffle, the deck is reset"); QUnit.equal(deck.skip(deck.totalNumOfCards()).availableNumOfCards(), 0, "skipping all cards gets no card left"); QUnit.equal(deck.getCard() == undefined, true, "call getCard with no card available gets no card");});
Finally, we only need to write a simple webpage to run the test:
QUnit Test Suite