Create Jasmine Test Harness Page in ASP.NET MVC

In this post I will document how to quickly create a test harness page in ASP.NET MVC project that runs JavaScript tests using Jasmine unit test framework.

First of all, let’s talk about project structure.  I have traditional ASP.MVC project and I am using Angular framework to build Single Page Application.  Hence, my project structure will have app folder, where all my Angular artifacts will go.  I have in parallel appSpec folder where all my Jasmine test scripts go.  I am also using naming convention for the test scripts, using the same name as main Angular script plus .spec suffix.  So, if my main script is called menu.js, my test script will be called menu.spec.js.  Again, this convention is recommended by the guys who wrote Angular.

image

Now, I wanted to create a quick test harness in MVC that would allow developers and QA to quickly run the tests, potentially based on some test filter/keyword.  I know we could use Karma runner, and it is on to-do list, but in the mean time, MVC harness will achieve the same thing.

To do this I will create Test MVC controller that will gather up all my scripts based on (optionally) partial file name.  There is only one action in the controller, and here it is.

    public class TestController : PublicController
    {
        public ActionResult Index(string fileName)
        {
            var data = new List<string>();
            var path = Server.MapPath("~/appSpec");
            var testScriptsFolder = new DirectoryInfo(path);
            var scripts = testScriptsFolder.GetFiles("*.spec.js", SearchOption.AllDirectories);
            foreach (var fileInfo in scripts)
            {
                if (fileInfo.DirectoryName != null &&
                    (string.IsNullOrEmpty(fileName) || 
                        fileInfo.Name.ToUpper().Contains(fileName.ToUpper())))
                {
                    data.Add(@"/appSpec" + fileInfo.DirectoryName.Replace(path, "")
                        .Replace(@"", @"/") + @"/" + fileInfo.Name);
                }
            }
            return View("Test", "_TestLayout", data);
        }
    }

As you can see, I am gathering a list of scripts to be injected into my view.  The view (Test.cshtml) code is very simple as well.

@model List<string>

@foreach (var script in Model)
{
    @Html.Raw("<script src="" + script + ""></script>");
}

Now, I am also creating a custom layout page, and it is called _TestLayout.cshtml as you can see from my View call.  It’s code simply contains all the needed scripts, some using bundles, and some, like Jasmine scripts are simply included.  The reason for that is that I am not going to deploy the  tests, and hence I do not need to have them bundled.  I am also going to use this page for HTML tests, so I am including the needed CSS.

@using System.Web.Optimization
@using My.Web.Controllers

<!DOCTYPE html>
<html data-ng-app="app.main">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Jasmine Tests</title>
    <img src="~/Content/jasmine/jasmine_favicon.png" />
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
    @Scripts.Render("~/bundles/thirdparty")
    <link href="~/Content/jasmine/jasmine.css" rel="stylesheet" />
    <script src="~/Scripts/jasmine/jasmine.js"></script>
    <script src="~/Scripts/jasmine/jasmine-html.js"></script>
    <script src="~/Scripts/angular-mocks.js"></script>
    <script src="~/appSpec/common/helpers/testHelper.js"></script>

    @Scripts.Render("~/app/modules")
    <script type="text/javascript">
        // this creates global modules that will be used by other modules 
        // to access some global variables stored in globals module.  We cannot use type script here.
        angular.module('app.globalsModule', []).factory('globalsService', function () {
            var globals = {};
            globals.baseUrl = '@(Url.Content(@"~/"))';
            globals.version = '@(typeof(HomeController).Assembly.GetName().Version.ToString())';
            globals.webApiBaseUrl = '@(Url.Content(@"~/api"))';
            return globals;
        });
    </script>
    @RenderBody()
</head>
<body>
    Test Runner
</body>

</html>


<script type="text/javascript">
    $(document).ready(function () {
        var jasmineEnv = jasmine.getEnv();
        jasmineEnv.updateInterval = 1000;

        var htmlReporter = new jasmine.HtmlReporter();

        jasmineEnv.addReporter(htmlReporter);

        jasmineEnv.specFilter = function (spec) {
            return htmlReporter.specFilter(spec);
        };

        jasmineEnv.execute();
    });
    (function () {
        

    })();
</script>

You see the call to RenderBody()?  This will contain my Test.cshtml view with all my scripts.  The very last portion of the page contains the script that will kick in Jasmine framework that will examine loaded tests scripts and will run them.  I can get to the page by typing localhost:11111/Test/ in my browser, assuming this where my site is setup.  I can also type /Test/?fileName=menu to have the loaded scripts filtered by test with “menu” in file name.  Quick and easy.

Thank you.

2 Comments

  1. Pingback: Testing Controllers in Angular with Jasmine | Sergey Barskiy's Blog

Leave a Reply

Your email address will not be published. Required fields are marked *