HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

Testing input validation and an AJAX request with Javascript and QUnit

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
ajaxwithjavascriptrequestvalidationqunitinputtestingand

Problem

I have a simple web page where the user enters the name and rank of a soldier and hits submit. If the input is invalid, an appropriate error message gets displayed. Otherwise, it adds the soldier to a database and adds it to a list of current Soldiers via AJAX request.

```
//Find elements on page
var rankField = $("input[name='rank']");
var nameField = $("input[name='name']");
var submit = $("input[type='submit']");
var list = $("ul");

//attach event-handler to submit button
submit.click(function() {
var rank = rankField.val();
var name = nameField.val();
onSoldierSubmitted(rank, name);
});

function onSoldierSubmitted(rank, name) {
clearErrors();

var rankIsValid = isValidRank(rank);
var hasErrors = false;

if (!rankIsValid) {
addError('rank is invalid');
hasErrors = true;
};

if (!name) {
addError('Please enter a name for your Soldier');
hasErrors = true;
};

if (hasErrors) {return;}
appendSoldier();
};

function clearErrors() {
$('.alert-danger').empty();
};

function addError(errorText) {
var errorDiv = $('.alert-danger');
var currentError = errorDiv.html();

if (currentError) {
currentError += '';
};

errorDiv.html(currentError + errorText);
};

function isValidRank(rank) {
var ranks = ['GEN', 'LTG', 'MG', 'BG', 'COL', 'LTC', 'MAJ',
'CPT', '1LT', '2LT', 'CW5', 'CW4', 'CW3', 'CW2', 'WO1',
'SMA', 'CSM', 'SGM', '1SG', 'MSG', 'SFC', 'SSG', 'SGT',
'CPL', 'SPC', 'PFC', 'PV2', 'PVT']
normalizedRank = rank.toUpperCase();
return ranks.indexOf(normalizedRank) != -1;
};

function appendSoldier() {
$.get("/ajax/soldier",
{
rank: rankField.val(),
name: nameField.val(),
}).done(
function(data) {
soldiers = extractSoldiersFromList(data);
renderHTMLListFromData(soldiers);
}
);

clearInput();
};

function extractSoldiersFromList(data) {
var soldierData = $.parseJSON(data);
var soldiers = [];

for (var i=0; i " + soldiers[i] + "");
}
}

function clear

Solution

My reasoning here is that unit tests aren't supposed to test anything
external

I agree with that. You can't test external things like an AJAX call anyway.


I'm unhappy with this, because it still doesn't tell me that
appendSoldier works like it's supposed to.

Actually, you CAN test the appendSoldier method. You need to refactor it in the following way:

function appendSoldier() {
    $.get("/ajax/soldier", 
    {
       rank: rankField.val(),
       name: nameField.val(),
    }).done( 
        function(data) {
            // Notice that I extracted these 2 lines of code into a separate method
            // soldiers = extractSoldiersFromList(data);
            // renderHTMLListFromData(soldiers);
            appendSoldierSuccess(data);
        }
    );

    clearInput();
};

// This is the extracted method
function appendSoldierSuccess(data) {
    soldiers = extractSoldiersFromList(data);
    renderHTMLListFromData(soldiers);
}


Now you can unit test the functionality in the following way (I will use Jasmine because I'm familiar with it but the concepts can be mapped to Sinon):

it("appendSoldierSuccess()", function() {
    var data = {};
    var soldiers = {};

    spyOn(window, 'extractSoldiersFromList').and.returnValue(soldiers);
    spyOn(window, 'renderHTMLListFromData');

    appendSoldierSuccess(data);

    expect(window.extractSoldiersFromList).toHaveBeenCalledWith(data);
    expect(window.renderHTMLListFromData).toHaveBeenCalledWith(soldiers);
});

Code Snippets

function appendSoldier() {
    $.get("/ajax/soldier", 
    {
       rank: rankField.val(),
       name: nameField.val(),
    }).done( 
        function(data) {
            // Notice that I extracted these 2 lines of code into a separate method
            // soldiers = extractSoldiersFromList(data);
            // renderHTMLListFromData(soldiers);
            appendSoldierSuccess(data);
        }
    );

    clearInput();
};

// This is the extracted method
function appendSoldierSuccess(data) {
    soldiers = extractSoldiersFromList(data);
    renderHTMLListFromData(soldiers);
}
it("appendSoldierSuccess()", function() {
    var data = {};
    var soldiers = {};

    spyOn(window, 'extractSoldiersFromList').and.returnValue(soldiers);
    spyOn(window, 'renderHTMLListFromData');

    appendSoldierSuccess(data);

    expect(window.extractSoldiersFromList).toHaveBeenCalledWith(data);
    expect(window.renderHTMLListFromData).toHaveBeenCalledWith(soldiers);
});

Context

StackExchange Code Review Q#80695, answer score: 2

Revisions (0)

No revisions yet.