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.

 



Housters easy homes for sale search

clock May 16, 2011 00:18 by author Justin Toth

I wanted to blog about a new web application I'm working on called Housters. I had been searching real estate using sites like zillow.com and realtor.com and had felt like neither did that great of a job. Realtor has a good amount of data, however they don't put any thought into how it's displayed so the user experience is poor. Zillow is much better about how they display the data they get, however they don't seem to have as much data as Realtor. 

I created Housters to feel like a google-esque search engine except only for homes for sale in the US. I ended up using jQuery, ASP.NET MVC 3, Razor, and C# for this task. For the backend, I originally was using SQL Server 2008, yet quickly realized that it wouldn't be up to the task, as I only have one server and there are 3.5 million homes for sale in the US right now. I switched to MongoDB, a "No SQL" implementation that is used by Craigslist, and WOW! It handles storing millions of homes for sale, not to mention the hundreds of millions of child collections I have for storing things like photos and sales history.. all on one server no problem!!

A few things that make Housters unique:

 

  • Ability to search and filter on a map.
  • Graphs of sales history, tax history, and price change history.
  • Ability to leave feedback on a property using Facebook comments.

 

Feel free to check out Housters and let me know what you think.



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.



SportsAlert Facebook Application

clock March 12, 2010 21:40 by author Justin Toth

A while ago I posted on developing facebook applications with ASP.NET MVC and the .NET Facebook API. At the time I was developing on a Windows XP box, which runs IIS 5. ASP.NET MVC routing is intended for IIS 6 and above, so it was a painful process. Now that I'm on Windows 7 with IIS 6.5 I decided to come back to that project and give it a try. Amazingly everything just started working without any code changes.

Without further ado, here is the SportsAlert Facebook application:

http://apps.facebook.com/sportsalert/

This app is very simple, allowing you to sign up for SportsAlert and subscribe to teams from within the Facebook app. For the teams you pick, you will get text messages at the end of the games. If you want to subscribe to individual games and/or change the frequency of your alerts (on score change, on quarter change, etc...) then you can login automatically to the SportsAlert web site from the facebook app.



Developing Facebook Applications with ASP.NET MVC

clock July 26, 2009 14:00 by author Justin Toth

As Facebook continues to grow at a rapid pace, more and more businesses have been signing on developers to build custom Facebook applications using Facebook's API's so that they can gain exposure to the Facebook masses and the extraordinary amount of personal data that those users have stored within Facebook.

I recently was asked to build a Facebook application for a project. Naturally, I wanted to find a nice solution that would work with my technology set, mainly ASP.NET, not Facebook's standard language: PHP. I even considered building the app using Silverlight, but decided against it since Silverlight is still so new and hasn't been installed yet by so many users. Since we're in the .NET 3.5 era, I decided to go with ASP.NET MVC rather than the standard web forms version of ASP.NET.

The first thing to do is to find a .NET Facebook framework. Like most people, when I first started searching around, I found two choices: Facebook.NET and the Facebook Developer Toolkit.

The Facebook Developer Toolkit is probably the more popular of the two but it has some shortcomings. From my reading, I got the overwhelming impression that the code base was poorly written by the creator, Clarity Consulting. Furthermore, there is no built-in support for ASP.NET MVC so you have to figure out how to make them mesh yourself. Lastly, many of the methods aren't up to date to match the Facebook API methods so you're on your own to update them manually. The Facebook API's change frequently so it's very hard for a .NET Facebook Famework to keep up with those changes, and the FDT doesn't seem to do too good a job of that.

Facebook.NET was written by a well-respected MSFT employee, Nikhil Kothari, and thus, the code base is nice and clean. It provides for much more flexibility than the FDT, hence it'd be easier to make it work with ASP.NET MVC. However, Nikhil seems to have ditched the project, so it hasn't been updated in a couple of years. That means that its methods no longer match the Facebook API methods.

I wasn't satisfied with either of these solutions so I kept looking and was extremely happy when I found a new project called the .NET Facebook API Client, which is still in Alpha. This project is specifically designed for ASP.NET MVC and provides a Visual Studio 2008 template that sets everything up for you, such as Facebook authentication and using Facebook Connect. The code is nice and clean and is provided to you so that if you run into bugs (since it's an Alpha release), you can modify the code yourself to get it working. Here's the best part - they built a tool that will automatically update the methods when the Facebook API methods change, so it will always match the Facebook API, something that is sorely missing from the big two that I mentioned above.

With the .NET Facebook API Client, I was able to build my first ASP.NET MVC Facebook application without running into too many issues. You can find the link to the app below, which has you enter in your email address, mobile #, and carrier, and then lets you pick your favorite sports teams. It will send you a text message at the end of each game for the teams you picked with the final scores.

http://apps.facebook.com/sportsalert/



Introduction

clock May 3, 2009 19:30 by author Justin Toth

This is my first post and I'm excited to start blogging!! It seems like every day I run into some sort of issue and once I figure out how to solve it, it makes me wish I had a blog so I could post the solution so that others won't have to spend as much time on it as I did.

This blog will be related to .NET development, and more specifically front-end web development. I used to consider myself an ASP.NET developer but I feel like we've come upon a crossroads. Straight ahead is ASP.NET, to the left is pure javascript development, and to the right is Silverlight. You look further down the ASP.NET road and see that there's another branch up beyond this one, ASP.NET going to the left and ASP.NET MVC going to the right. Which road to take??



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