Justin Toth's Blog

Justin is a web developer living in Maryland

Running nunit test cases from an ASP.NET MVC web app

clock August 25, 2011 13:01 by author Justin Toth

 

I had a goal of being able to run nunit test cases from within my ASP.NET MVC web app, like what some CI tools like teamCity do, except within my own app and in a way that I can customize the output.

 

I first tried starting the nunit-console process using Process.Start and redirecting its output to my web app, however this is not a good solution because the redirected output from the Process class is buffered and you have no control over when it flushes the buffer. I found certain programs like msbuild work great and it flushes constantly, however with nunit-console it holds onto the output until all test cases are complete, which means you can't see the progress of the test cases as they run.

 

The solution is to use the RemoteTestRunner nunit class and create an event listener class that implements the NUnit.Core.EventListener interface:

 

    public class NUnitEventListener : NUnit.Core.EventListener

        {

            public event EventHandler CompletedRun;

            public StringBuilder Output;

            private int TotalTestsPassed = 0;

            private int TotalTestsErrored = 0;

 

            public void RunStarted(string name, int testCount)

            {

                Output.AppendLine(TimeStamp + "Running " + testCount + " tests in " + name + "<br/><br/>");

                TotalTestsPassed = 0;

                TotalTestsErrored = 0;

            }

 

            public void RunFinished(System.Exception exception)

            {

                Output.AppendLine(TimeStamp + "Run errored: " + exception.ToString() + "<br/>");

                //notify event consumers.

                if (CompletedRun != null)

                    CompletedRun(exception, new EventArgs());

            }

 

            public void RunFinished(TestResult result)

            {

                Output.AppendLine(TimeStamp + "<label class='normal " + (TotalTestsErrored == 0 ? "green" : "red")

                    + "'>" + TotalTestsPassed + " tests passed, " + TotalTestsErrored + " tests failed</label><br/>");

                Output.AppendLine(TimeStamp + "Run completed in " + result.Time + " seconds<br/>");

                //notify event consumers.

                if (CompletedRun != null)

                    CompletedRun(result, new EventArgs());

            }

 

            public void TestStarted(TestName testName)

            {

                Output.AppendLine(TimeStamp + testName.FullName + "<br/>");

            }

 

            public void TestOutput(TestOutput testOutput)

            {

                if(testOutput.Text.IndexOf("NHibernate:") == -1)

                    Output.AppendLine(TimeStamp + testOutput.Text + "<br/>");

            }

 

            public void TestFinished(TestResult result)

            {

                if (result.IsSuccess)

                {

                    Output.AppendLine(TimeStamp + "<label class='green normal'>Test Passed!</label><br/><br/>");

                    TotalTestsPassed++;

                }

                else

                {

                    Output.AppendLine(TimeStamp + "<label class='red normal'>Test Failed!<br/>" + result.Message.Replace(Environment.NewLine, "<br/>") + "</label><br/>");

                    TotalTestsErrored++;

                }

            }

 

            public void UnhandledException(System.Exception exception)

            {

                Output.AppendLine(TimeStamp + "Unhandled Exception: " + exception.ToString() + "<br/>");

            }

 

            public void SuiteStarted(TestName testName)

            {

            }

 

            public void SuiteFinished(TestResult result)

            {

            }

 

            private string TimeStamp

            {

                get

                {

                    return "[" + DateTime.Now.ToString() + "] ";

                }

            }

        }

 

After that, create a TestRunner class that calls RemoteTestRunner and uses your sexy new event listener class:

 

    public static class TestRunner

        {

            public static bool InProgress = false;

            public static StringBuilder Output = new StringBuilder();

            private static RemoteTestRunner Runner;

 

            public static void Start(string fileName)

            {

                InProgress = true;

                Output = new StringBuilder();

                StartTests(fileName);

            }

 

            private static void StartTests(string fileName)

            {

                //start nunit.

                var testPackage = new TestPackage(fileName);

                Runner = new RemoteTestRunner();

                Runner.Load(testPackage);

                var nunitEventListener = new NUnitEventListener();

                nunitEventListener.CompletedRun += new EventHandler(nunitEventListener_CompletedRun);

                nunitEventListener.Output = Output;

                Runner.BeginRun(nunitEventListener);

            }

 

            static void nunitEventListener_CompletedRun(object sender, EventArgs e)

            {

                if (Runner != null)

                {

                    Runner.CancelRun();

                    Runner = null;

                }

                InProgress = false;

            }

        }

 

Now call the TestRunner class in your ASP.NET MVC Controller:

 

    public class TestController : ApplicationController

        {

            //GET: /Test/Index/

            public ActionResult Index()

            {

                TestRunner.Start(@"C:\PathToTestProject\bin\Release\SystemTest.dll");

                return View();

            }

 

            //POST: /Test/GetOutput/

            [AcceptVerbs(HttpVerbs.Post)]

            public ActionResult GetOutput()

            {

                var result = new

                {

                    InProgress = TestRunner.InProgress,

                    Output = TestRunner.Output.ToString()

                };

                return Json(result);

            }

        }

 

Lastly, create a simple view to show the output as the test cases run. My example uses dojo but it could easily be modified to use jquery or vanilla javascript:

 


    <script type="test/javascript">
    var nunit = {
 
        init: function () {
 
            nunit.get();
 
        },
 
        get: function () {
 
            //ajax call.
            ajax.post("test/getoutput/", {}, nunit.display);
 
        },
 
        display: function (result) {
 
            console.debug(result);
            dojo.byId("output").innerHTML = result.Output.length > 0 ? result.Output : dojo.byId("output").innerHTML;
            if (result.InProgress)
                window.setTimeout(nunit.get, 10000);
 
        }
 
    };
 
    dojo.addOnLoad(nunit.init);
    </script>
 
    <div id="output">
        The tests are running, please wait....
    </div>

 

That's it... Hope this helps some others, as all of the examples online of RemoteTestRunner (including on stackoverflow) pass in a NullListener, which means you can't capture the output of the test run.

 



ASP.NET MVC + Dojo's Dijit.Form for Form Validation and Submission

clock April 25, 2010 20:03 by author Justin Toth

One of the major hurdles when you switch over from ASP.NET web forms to ASP.NET MVC is deciding how to handle your submission forms. You no longer have all of the input controls and the nice validation controls, so you may be a little lost as to how to proceed. Doing server-side validation isn't enough anymore in the ajax world we live in. Writing custom javascript code to handle all of the validation would be a huge undertaking. Luckily, Dojo comes to the rescue with its Dijit.Form widgets. Below is what a login form would look like using dojo:


 
<div dojoType="dijit.form.Form" jsId="loginForm" 
            encType="multipart/form-data" action="" method="">
            <script type="dojo/method" event="onSubmit">
                if (this.validate()) {
                    SubmitLoginForm(loginForm.getValues());
                }
                return false;
            </script>
 
            <table cellspacing="0">
                <tr>
                    <td class="label">Username:</td>
                    <td class="input mediumfield">
                        <input id="tbUsername" name="tbUsername" type="text" size="20"
                            dojoType="dijit.form.ValidationTextBox"
                            required="true"
                            promptMessage="Enter Username."
                            invalidMessage="Username is required." />
                    </td>
                </tr>
                <tr>
                    <td class="label">Password:</td>
                    <td class="input mediumfield">
                        <input id="tbPassword" name="tbPassword" type="password" size="20"
                            dojoType="dijit.form.ValidationTextBox"
                            required="true"
                            promptMessage="Enter Password."
                            invalidMessage="Password is required." />
                    </td>
                </tr>
            </table>
 
            <table id="buttons" class="buttons" cellspacing="0">
                <tr>
                    <td class="center">
                        <button dojoType="dijit.form.Button" type="submit" value="Submit">Login</button>
                    </td>
                </tr>
            </table>
 
        </div>
 

 

As you can see, you create a dijit.form.Form widget, which will act as our <form> element normally would. When the login button is clicked, it'll validate all of the controls within the form and then call a javascript method called SubmitLoginForm. You'll notice that my dojo method for onSubmit returns false. Normally you would have it return false if the form was invalid and true otherwise, but this would actually submit the form and do a server-side postback. Seeming that we're using purely ajax to make our calls in this example, we're returning false regardless of if the form is valid or not so that it won't postback to the server.

Next we need to write our javascript to submit the login form using ajax:


    dojo.require("dojo.parser");
    dojo.require("dijit.form.Form");
    dojo.require("dijit.form.ValidationTextBox");
    dojo.require("dijit.form.Button");
    dojo.addOnLoad(SetFocus);
    function SetFocus() {
        //find controls.
        var tbUsername = dojo.byId("tbUsername");
        //set focus.
        tbUsername.focus();
    }
    function SubmitLoginForm(fields) {
        //ajax call.
        var request = { "userName": fields.tbUsername, "password": fields.tbPassword };
        Post("login/validate/", request, FormResult);
    }
    function FormResult(result) {
        if (result.indexOf("Error") > -1) {
            //TODO: show error.
        }
        else {
            //redirect.
            window.location = result;
        }
    }
 

 

The cool part is that when you call loginForm.getValues(), it returns an object containing the values of all of the form fields. Within the SubmitLoginForm method we're able to easily create an ajax request and post it back to the server. Above I omitted the function declaration for Post, so below I'm showing what mine looks like. It can be a lot simpler if you only want to make one ajax call, but my example below will try up to 5 times to make the ajax call, in case there are some sort of flukey issues that cause your ajax requests to fail every now and then:

 


 
var maxAjaxCalls = 5;
var currentAjaxCalls = new Array();
 
function Post(path, request, callbackFunction) {
    //increment current ajax calls.
    currentAjaxCalls[currentAjaxCalls.length] = path;
    //make sure hasn't gone over max ajax calls.
    var numAjaxCall = NumAjaxCalls(path);
    if (numAjaxCall <= maxAjaxCalls) {
        if (numAjaxCall > 1) {
            console.debug("Invoking " + path + " ajax call for try #" + numAjaxCall);
        }
        dojo.xhrPost({
            url: baseUrl + path,
            handleAs: 'json',
            timeout: 60000,
            content: request,
            contentType: "application/x-www-form-urlencoded",
            load: function(result) { PostSuccess(result, path, callbackFunction); },
            error: function(error, args) { AjaxError(error, args, path, request, callbackFunction); }
        });
    }
    else {
        console.debug("Tried to make " + path + " ajax call " + maxAjaxCalls + " times but failed!");
    }
}
 
function NumAjaxCalls(path) {
    var numAjaxCalls = 0;
    for (var k = 0; k < currentAjaxCalls.length; k++) {
        if (currentAjaxCalls[k] == path) {
            numAjaxCalls++;
        }
    }
    return numAjaxCalls;
}
 
function PostSuccess(result, path, callbackFunction) {
    //remove path from array.
    for (var k = 0; k < currentAjaxCalls.length; k++) {
        if (currentAjaxCalls[k] == path) {
            currentAjaxCalls.splice(k, 1);
            k--;
        }
    }
    //invoke callback function.
    callbackFunction(result);
}
 
function AjaxError(error, args, path, request, callbackFunction) {
    //show error message.
    //console.debug("ajax error: " + error.message + " " + error.responseText);
    //console.debug(error);
    //try ajax call again in 1 second.
    window.setTimeout(function() { Post(path, request, callbackFunction); }, 1000);
}
 

 

The last step would be to create your LoginController and create a method called Validate:

 

//GET: /Login/Validate/

        public ActionResult Validate(string userName, string password, string returnUrl)

        {

            string result = String.Empty;

            try

            {

                if(IsAuthenticated)//TODO: check if they're authenticated.

                    result = "someurltoredirectto";

                }

                else

                {

                    result = "Error: Authentication failed, check your Username and Password.";

                }

            }

            catch (Exception ex)

            {

                result = "Error: " + ex.Message;

            }

            return Json(result);

        }

 

 

Pretty simple stuff. In this way you're able to save yourself from having to write a lot of code, letting dojo do the majority of the heavy lifting. If you were to use this example, you'd obviously want to make sure you took care of server-side validation as well, in case someone disabled javascript.



Clean up your JavaScript Code with Dojo

clock December 19, 2009 19:39 by author Justin Toth

You may have heard of Dojo, the javascript toolkit found at http://www.dojotoolkit.org/. The difference between it and its competitors such as jQuery and Ext JS is that it's much more than a toolkit, it's a full-fledged javascript framework. It provides things such as object-oriented programming (who would've thought you'd ever be able to have class inheritance in javascript?!), template-based widgets (you can dynamically create widgets in javascript that have html templates rather than having to dynamically create the tr's and td's with DOM manipulation), and basic utility functionality.

I've been using the more complex features of dojo, such as OO and template-based widgets, for some years but I never bothered with the basic utility functionality until today. It's pretty cool what dojo lets you do and it makes me sad that I was writing manual javascript code for years to do the same things. Here are a few of the cooler things that dojo lets you do:

1. Finding and looping through DOM elements

dojo.forEach(
                dojo.query("#myDiv img"),
                function(element) {
                    dojo.attr(element, { src: "_images/test.png" });
                }
            );

You can see the use of 3 dojo functions here: dojo.forEach, dojo.query, and dojo.attr. This will loop through all img elements that are children of the element with id "myDiv" and set the src of each image. Dojo.query is very powerful and can let you easily select the element(s) that you want from the page or from a specific parent node.

2. Creating DOM elements

var imgArrow = dojo.create("img", { src: "_images/arrow.png", title: "my arrow!" }, divContainer);

This code creates an image, sets the src and title of it, and appends it to a div, all in 1 line of code!

3. Clearing DOM elements

 dojo.empty(divContainer);

Dojo.empty clears all child elements of the element inputted, no more grabbing all child elements, looping through them, and doing childNode.parentNode.removeChild(childNode).

I've barely scratched the surface of what you can do with dojo but you can go a long way with just these 3 concepts...



About the author

Justin

Justin is a senior developer who has been working with .NET since 2003. His main focus is building highly-interactive web applications using ASP.NET MVC and Dojo or jQuery. Visit his company's site at http://tothsolutions.com.

Page List

Sign in