Categories
Programming

Include JavaScript and TypeScript Tests in Visual Studio

Four years on from the original article, I thought I’d update this article to reflect a few important changes about running TypeScript or JavaScript tests in Visual Studio. Firstly, let’s summarise the things that changed since 2013.

First and foremost, I said the instruction apply to JavaScript, TypeScript, and even CoffeeScript. In 2017, you probably aren’t using CoffeeScript. It dropped into the most dreaded languages in the Stack Overflow annual survey, and all the greatest frameworks these days are leaning towards, well, TypeScript.

Secondly, I’m not really using tsUnit so much these days. I use Jest, and Jasmine a fair bit; but for pure BDD-style specifications, I use TypeSpec BDD. So yeah, I’m still using a test framework that I wrote myself.

And thirdly, lastly, and all-importantly… task runners have taken over the world. Literally, I don’t even scratch my own cheeks these days as I have a Gulp task that does it for me. It’s just how things are done now.

So, part one of this article is dedicated to running your JavaScript and TypeScript tests in Visual Studio using a task runner. Part two shows the old-skool way that your ancestors used.

For brevity, I’ll just say “JavaScript” for the rest of this article, even though your best JavaScript is now generated by the TypeScript compiler.

JavaScript Tests in Visual Studio

Let’s use Jest and Gulp for a practical example. The idea applies equally to pretty much any test framework.

Add Gulp to your package.json file, as even if you have it globally you’ll need it locally too.

To run Gulp tasks, all you need is a file named gulpfile.js. So add one if you need one, and then add a task to run the Jest tests.

const gulp = require('gulp');
const jest = require('gulp-jest').default;

gulp.task('test', function () {
    return gulp.src('./').pipe(jest());
});

Once you have a Gulp task file, you can open the Visual Studio Task Runner Explorer window. Use CTRL + ALT + Backspace to open the Task Runner Explorer if you need to.

You’ll see the task show up as shown below:

JavaScript Tests in Visual Studio with Gulp

To make things really sweet, you will probably want to run your tests every time you build. Just right click on the “test” task in Task Runner Explorer and select “Bindings” -> “After Build”. This just adds the following comment to the top of your Gulp file, which Visual Studio uses to hook into the tasks:

/// <binding AfterBuild='test' />

If you have multiple tasks to run on build, it is typical to wrap them together in a default task.

And that’s about it. Your tests can now be run within Visual Studio, and automatically each time you build your solution. Neat.

Part Two – Old Skool

This old skool method allows you to get your JavaScript tests wrapped in a C# test. You can then run it just like your other .NET tests. This is how I tested my web-based JavaScript tests, as many test frameworks used to run within a browser.

So normally for tsUnit, you have a web page to display your results and you include your scripts and tests – like this:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Tests</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta name="author" content="Steve Fenton">
    <style>
        body
        {
            font-family: 'Segoe UI', sans-serif;
        }
        h1, h2
        {
            font-weight: normal;
        }
        span
        {
            font-style: italic;
        }
        .good
        {
            color: #8CBF26;
        }
        .bad
        {
            color: #E51400;
        }
        li
        {
            font-weight: bold;
        }
            li li
            {
                font-weight: normal;
                color: #666;
            }
    </style>
</head>
<body>
    <div id="results"></div>
    <script src="Encoding.js"></script>
    <script src="Logging.js"></script>
    <script src="tsUnit.js"></script>
    <script src="tsTests.js"></script>
</body>
</html>

So how do we run the tests in Visual Studio instead?

JavaScript Helper Class

This is an adapted version of a class I found online a while back. This is a re-usable class that will run your JavaScript inside of a “MSScriptControl”.

using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MSScriptControl;

namespace MyApp.Tests
{
    [ExcludeFromCodeCoverage]
    public class JavaScriptTestHelper : IDisposable
    {
        private ScriptControl _script;
        private TestContext _context;
        
        public JavaScriptTestHelper(TestContext testContext)
        {
            _context = testContext;
            _script = new ScriptControl();
            _script.Language = "JScript";
            _script.AllowUI = false;
            LoadShim();
        }
        
        public void LoadFile(string path)
        {
            var fileContents = File.ReadAllText(path);
            _script.AddCode(fileContents);
        }
        
        public void LoadFiles(string[] paths)
        {
            foreach(string path in paths) {
             LoadFile(path);
            }
        }
        
        public void ExecuteTest(string testMethodName)
        {
            dynamic result = null;
            try
            {
                result = _script.Run(testMethodName, new object[] { });
            }
            catch
            {
                var error = ((IScriptControl)_script).Error;
                if (error != null && _context != null)
                {
                    _context.WriteLine(String.Format("{0} rnLine: {1} Column: {2}", error.Source, error.Line, error.Column));
                }
                throw new AssertFailedException(error.Description);
            }
        }
        
        public void Dispose()
        {
            _script = null;
        }
        
        private void LoadShim()
        {
            _script.AddCode("var isMsScriptEngineContext = true; var window = window || {}; var document = document || {};");
        }
    }
}

Just put this class somewhere your test project can get hold of.

Reference Your Scripts

In my test project, I created a folder named “ReferencedScripts” and referenced the scripts from the project being tested (add existing item > add as link). Set the file to copy to the output folder.

In my case, I’m referencing the following:

  • Logging.js
  • Encoding.js
  • Http.js
  • Ajax.js
  • tsUnit.ts (The unit testing framework)
  • tsTests.js (The actual tests)

The Unit Tests

You can now write a single unit test that runs all of the tests in you wrote in JavaScript (TypeScript, CoffeeScript or whatever!).

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace MyApp.Tests
{
    [TestClass]
    [DeploymentItem("ReferenceScripts", "ReferenceScripts")]
    public class Scripts
    {
        private TestContext _context;
        
        public TestContext TestContext
        {
            set { this._context = value; }
        }
        
        [TestMethod]
        public void DecisionScriptsJavaScriptTests()
        {
            var jsHelper = new JavaScriptTestHelper(_context);
            // Load JavaScript files
            jsHelper.LoadFiles(new [] {
                "ReferenceScripts\\Logging.js",
                "ReferenceScripts\\Encoding.js",
                "ReferenceScripts\\Http.js",
                "ReferenceScripts\\Ajax.js",
                "ReferenceScripts\\tsUnit.js",
                "ReferenceScripts\\tsTests.js"});
            // Execute JavaScript Test
            jsHelper.ExecuteTest("getResult");
        }
    }
}

If the test fails, you’ll get reasonable information about the failure in your Visual Studio test window.