The Practical Client
Test-Driven Development with TypeScript
Set up a TDD environment for test applications by choosing and integrating a toolset that works with Visual Studio. It's not a completely satisfying solution, but it's enough to start building an application.
I'll begin with an update to my last column: TypeScript is still beta software. The beta issue came up when Visual Studio stopped compiling my TypeScript files, and I switched to using the command-line compiler. In addition, I stopped being able to find my TypeScript templates in the Visual Studio Add New dialog. Of course, I'm never sure how many of my beta problems are due to unstable beta software, beta software not yet being fully built out to my expectations, or the person between the chair and keyboard not knowing what he's doing.
In the end, I uninstalled and re-installed the TypeScript package. Once I did that, my TypeScript files went back to being compiled when I saved them. I even found my TypeScript templates again (though I have to admit they may never have been missing). The TypeScript templates appeared in the C# files node in the Add New Item dialog's TreeView, rather than in the Web node (where I think I had been looking for them).
I also installed Mads Kristensen's Web Essentials from NuGet, which (among other things) gave me a split-screen view of my TypeScript file with its related JavaScript files. (That made it a lot easier to write this column.) Web Essentials also gave me something else I wanted: an Options list for configuring my TypeScript compiles (just go to Tools | Options | Web Essentials | TypeScript). I turned on the option to have my TypeScript files compiled when I built my application because, well, that's what I expect to happen when I build an application.
I don't want to suggest that all of these tools work together perfectly. Error messages from my TypeScript compiles do show up in the Error window, as you would expect. However, the Output window shows that everything has compiled successfully even when my TypeScript compiles fail. The Web Essentials split view that shows me the JavaScript code for my TypeScript code doesn't refresh nearly as often as I'd like and, as a result, is often out of sync with my TypeScript code.
But, as I said, it's beta software.
Test-Driven Development and a TypeScript Toolset
I'm a big fan of test-driven development (TDD). After I write some code, I want to test it to prove it works. It's gotten to the point that if I have to test code by actually running and interacting with the application's UI, I get frustrated with how much time I feel I'm wasting. I even use the TDD framework that comes with Visual Studio when I'm debugging: It's so much easier, after setting a breakpoint, to right-click on the appropriate test and pick Debug Tests than it is to run the application (this is especially true with the new Text Explorer window in Visual Studio 2012).
But, to do TDD with TypeScript, I need two tools: one for creating tests and one for running those tests. Currently, out of the box, Visual Studio supplies neither of those tools for TypeScript. To begin with, TypeScript doesn't define classes in the same sense that C# or Visual Basic do. You can't, for instance, reference a TypeScript object from C# or Visual Basic code. At the very least, this means I have to write my test code in JavaScript. I'd prefer, of course, to write my test code in TypeScript.
You also can't, in your Test project, add a reference to your application project and pick up your JavaScript or TypeScript code. Yet, I want to keep all of my tests (server- and client-side) in a single Test project. That means I need to use file references from my JavaScript test code that point to the JavaScript files in my application project. This seems awfully brittle to me: If I restructure the projects in my Solution, I can see those references failing.
Within those limitations, I went looking for tools and discovered that I could stick with the ones I already know.
I've been using Matthew Manela's Chutzpah as my test runner for JavaScript tests for some time now. Chutzpah has two things I like: it supports tests written in the test framework that I use (QUnit, though I'm told that Chutzpah also works with Jasmine), and it integrates with Visual Studio. In fact, if you're working with Visual Studio 2012, you can add the Chutzpah add-in that integrates Chutzpah with Visual Studio Test Explorer.
With QUnit providing a framework for writing tests and Chutzpah providing a tool for running them, you'd think I'd be happy. But I also want to write my tests in TypeScript. That means that I need a way to integrate TypeScript and QUnit, which led me to Boris Yankov's DefinitelyTyped libraries.
DefinitelyTyped is a repository of TypeScript definition files (these are the files with an extension of .d.ts that you'll see cropping up with your .ts files). TypeScript definition files describe some set of JavaScript code to TypeScript and to Visual Studio. Most of the JavaScript libraries I work with have definition files available through DefinitelyTyped and, to make my life even easier, I can add them to my projects with NuGet.
So, after adding a Test project to my Solution, I used NuGet to add QUnit, the QUnit definition files from DefinitelyTyped, and Chutzpah to the project. I added the Chutzpah Test Adapter (which integrates Chutzpah with Visual Studio Test Explorer) from the Visual Studio Tools | Extensions and Update menu choice.
(I'm not suggesting that QUnit or Chutzpah are the best tools available, though I do like them both very much -- they're just what I'm using.)
Writing Tests
With the tools in place, I can start testing my code. I begin by checking to see if my Customer object's properties work correctly (I know it's hard to imagine I could screw that up; but trust me, I could find a way). To hold that first set of tests, I add a TypeScript file called CustomerTests.ts to my Test project.
In that TypeScript file, I need a reference to the TypeScript file in my application project holding the code I want to test (AdventureWorksEntities.ts). That reference performs two functions: It allows QUnit to find the code it's supposed to test, and it gives me IntelliSense support for the objects I've defined in those files while I'm writing the tests for them. I also need a reference to the QUnit definition file to get IntelliSense support for QUnit.
The following code, placed at the top of my CustomerTest.ts file, does the job (to reduce the brittleness of the file references, I use relative references to tie my test code to my application code file):
/// <reference path="../PJSalesOrder/Scripts/AdventureWorksEntities.ts"/>
/// <reference path="Scripts/typings/qunit/qunit.d.ts" />
Now I need to write a test. In QUnit, you use the test method, passing a description of the test (which will appear in Test Explorer) and a function to execute. That function will, after working with the application's code, typically finish with a call to the QUnit "ok" method, which allows you to test if you got the result you expected. The ok function needs to be passed a test and, optionally, a message to display if the test fails.
This example creates a test called "Id property on creating CUSTOMER." The test first creates a Customer object, setting the Id property through the constructor to 123. The test finishes by retrieving the Id property to see if it was successfully set to 123:
test("Id property on creating CUSTOMER ", function () {
var cust = new AdventureWorksEntities.Customer(123,"Peter","Vogel");
ok(cust.Id==123, "Customer ID not set correctly");
});
Before running my test, I have to save my Test project's files to cause my TypeScript test code to be recompiled. If I've made changes to my application's TypeScript code, I also need to save the application. I can see that getting into the habit of saving all the necessary projects is going to be painful -- I'm used to Visual Studio automatically saving and compiling all my changes before running a test.
Once my TypeScript test code has compiled, my test appears in Visual Studio Test Explorer. I then right-click on the test in Test Explorer, select Run Selected Tests to have my test execute and report its result. If the test fails, I can click on the test in Test Explorer and see the error message. I'm happy!
Well, almost happy -- I can't debug my tests. I can do some things: I can set a breakpoint in my TypeScript code; I can right-click on a TypeScript test and pick Debug Tests; and, when I run my test, Visual Studio will even throw itself into debug mode and load the application's symbol table. It just won't stop on my breakpoints. Sigh.
Still, I can now test my code as I write it. So, next column, I'll start building out the application and look at whether I can get debugging support when my code is executing in the browser.
About the Author
Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.