Thursday, 22 September 2016

TypeScript 2.0, ES2016 and Babel

TypeScript 2.0 has shipped! Naturally I'm excited. For some time I've been using TypeScript to emit ES2015 code which I pass onto Babel to transpile to ES "old school". You can see how here.

Merely upgrading my package.json to use "typescript": "^2.0.3" from "typescript": "^1.8.10" was painless. TypeScript now supports ES2016 (the previous major release 1.8 supported ES2015). I wanted to move on from writing ES2015 to writing ES2016 using my chosen build process. Fortunately, it's supported. Phew. However, due to some advances in ecmascript feature modularisation within the TypeScript compiler the upgrade path is slightly different. I figured that I'd just be able to update the target in my tsconfig.json to "es2016" from "es2015", add in the ES2016 preset for Babel and jobs a good 'un. Not so. There were a few more steps to follow. Here's the recipe:

tsconfig.json changes

Well, there's no "es2016" target for TypeScript. You carry on with a target of "es2015". What I need is a new entry: "lib": ["dom", "es2015", "es2016"]. This tells the compiler that we're expecting to be emitting to an environment which supports a browser ("dom"), and both ES2016 and ES2015. Our "environment" is Babel and it's going to pick up the baton from this point. My complete tsconfig.json looks like this:

{
  "compileOnSave": false,
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "lib": ["dom", "es2015", "es2016"],
    "jsx": "preserve",
    "module": "es2015",
    "moduleResolution": "node",
    "noEmitOnError": false,
    "noImplicitAny": true,
    "preserveConstEnums": true,
    "removeComments": false,
    "suppressImplicitAnyIndexErrors": true,
    "target": "es2015"
  }
}

Babel changes

I needed the Babel preset for ES2016; with a quick npm install --save-dev babel-preset-es2016 that was sorted. Now just to kick Webpack into gear...

Webpack changes

My webpack config plugs together TypeScript and Babel with the help of ts-loader and babel-loader. It allows the transpilation of my (few) JavaScript files so I can write ES2015. However, mainly it allows the transpilation of my (many) TypeScript files so I can write ES2015-flavoured TypeScript. I'll now tweak the loaders so they cater for ES2016 as well.

var webpack = require('webpack');

module.exports = {
  // ....

  module: {
    loaders: [{
      // Now transpiling ES2016 TS
      test: /\.ts(x?)$/,
      exclude: /node_modules/,
      loader: 'babel-loader?presets[]=es2016&presets[]=es2015&presets[]=react!ts-loader'
    }, {
      // Now transpiling ES2016 JS
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'babel',
      query: {
        presets: ['es2016', 'es2015', 'react']
      }
    }]
  },

  // ....
};

Wake Up and Smell the Jasmine

And we're there; it works. How do I know? Well; here's the proof:

  it("Array.prototype.includes works", () => {
    const result = [1, 2, 3].includes(2);
    expect(result).toBe(true);
  });

  it("Exponentiation operator works", () => {
    expect(1 ** 2 === Math.pow(1, 2)).toBe(true);
  });

Much love to the TypeScript team for an awesome job; I can't wait to get stuck into some of the exciting new features of TypeScript 2.0. strictNullChecks FTW!

Monday, 12 September 2016

Integration Tests with SQL Server Database Snapshots

Once More With Feeling

This is a topic that I have written about before.... But not well. I recently had cause to dust down my notes on how to use snapshotting in your integration tests. To my dismay, referring back to my original blog post was less helpful than I'd hoped. Now I've cracked the enigma code that my original scribings turned out to be, it's time to turn my relearnings back into something genuinely useful.

What's the Scenario?

You have a test database. You want to write integration tests. So what's the problem? Well, these tests will add records, delete records, update records within the tables of the database. They will mutate the data. And that's exactly what they ought to do; they're testing that our code uses the database in the way we would hope and expect.

So how do we handle this? Well, we could handle this by writing code at the end of each test that is responsible for reverting the database back to the state that it was in at the start of the test. So if we had a test that added a record and tested it, we'd need the test to be responsible for removing that record before any subsequent tests run. Now that's a totally legitimate approach but it adds tax. Each test becomes more complicated and requires more code.

So what's another approach? Perhaps we could take a backup of our database before our first test runs. Then, at the end of each test, we could restore our backup to roll the database back to its initial state. Perfect, right? Less code to write, less scope for errors. So what's the downside? Backups are slowwwww. Restores likewise. We could be waiting minutes between each test that runs. That's not acceptable.

There is another way though: database snapshots - a feature that's been nestling inside SQL Server for a goodly number of years. For our use case, to all intents and purposes, database snapshots offers the same functionality as backups and restores. You can backup a database (take a snapshot of a database at a point in time), you can restore a database (roll back the database to the point of the snapshot). More importantly, you can do either operation in *under a second*. As it happens, Microsoft advocate using this approach themselves:

In a testing environment, it can be useful when repeatedly running a test protocol for the database to contain identical data at the start of each round of testing. Before running the first round, an application developer or tester can create a database snapshot on the test database. After each test run, the database can be quickly returned to its prior state by reverting the database snapshot.

Sold!

Talk is cheap, show me the code

In the end it comes down to 3 classes; DatabaseSnapshot.cs which does the actual snapshotting work and 2 classes that make use of it.

DatabaseSnapshot.cs

This is our DatabaseSnapshot class. Isn't it pretty?

using System.Data;
using System.Data.SqlClient;

namespace Testing.Shared
{
    public class DatabaseSnapshot
    {
        private readonly string _dbName;
        private readonly string _dbSnapShotPath;
        private readonly string _dbSnapShotName;
        private readonly string _dbConnectionString;

        public DatabaseSnapshot(string dbName, string dbSnapshotPath, string dbSnapshotName, string dbConnectionString)
        {
            _dbName = dbName;
            _dbSnapshotPath = dbSnapshotPath;
            _dbSnapshotName = dbSnapshotName;
            _dbConnectionString = dbConnectionString;
        }

        public void CreateSnapshot()
        {
            if (!System.IO.Directory.Exists(_dbSnapshotPath))
                System.IO.Directory.CreateDirectory(_dbSnapshotPath);

            var sql = $"CREATE DATABASE { _dbSnapshotName } ON (NAME=[{ _dbName }], FILENAME='{ _dbSnapshotPath }{ _dbSnapshotName }') AS SNAPSHOT OF [{_dbName }]";

            ExecuteSqlAgainstMaster(sql);
        }

        public void DeleteSnapshot()
        {
            var sql = $"DROP DATABASE { _dbSnapshotName }";

            ExecuteSqlAgainstMaster(sql);
        }

        public void RestoreSnapshot()
        {
            var sql = "USE master;\r\n" +

                $"ALTER DATABASE {_dbName} SET SINGLE_USER WITH ROLLBACK IMMEDIATE;\r\n" +

                $"RESTORE DATABASE {_dbName}\r\n" +
                $"FROM DATABASE_SNAPSHOT = '{ _dbSnapshotName }';\r\n" +

                $"ALTER DATABASE {_dbName} SET MULTI_USER;\r\n";

            ExecuteSqlAgainstMaster(sql);
        }

        private void ExecuteSqlAgainstMaster(string sql, params SqlParameter[] parameters)
        {
            using (var conn = new SqlConnection(_dbConnectionString))
            {
                conn.Open();
                var cmd = new SqlCommand(sql, conn) { CommandType = CommandType.Text };
                cmd.Parameters.AddRange(parameters);
                cmd.ExecuteNonQuery();
                conn.Close();
            }
        }
    }
}

It exposes 3 methods:

CreateSnapshot
This method creates the snapshot of the database. We will run this right at the start, before any of our tests run.
DeleteSnapshot
Deletes the snapshot we created. We will run this at the end, after all our tests have finished running.
RestoreSnapshot
Restores the database back to the snapshot we took earlier. We run this after each test has completed. This method relies on a connection to the database (perhaps unsurprisingly). It switches the database in use away from the database that is being restored prior to actually running the restore. It happens to shift to the master database (I believe that's entirely incidental; although I haven't tested).
SetupAndTeardown.cs

This class is responsible for setting up the snapshot we're going to use in our tests right before any of the tests have run (in the FixtureSetup method). It's also responsible for deleting the snapshot once all the tests have finished running (in the FixtureTearDown method). It should be noted that in this example I'm using NUnit and this class is written to depend on the hooks NUnit exposes for running code at the very beginning and end of the test cycle. All test frameworks have these hooks; if you're using something other than NUnit then it's just a case of swapping in the relevant attribute (everything tends to attribute driven in the test framework world).

using NUnit.Framework;

namespace Testing.Shared
{
   [SetUpFixture]
   public class SetupAndTeardown
   {
      public static DatabaseSnapshot DatabaseSnapshot;

      [SetUp]
      public void FixtureSetup()
      {
         DatabaseSnapshot = new DatabaseSnapshot("MyDbName", "C:\\", "MySnapshot", "Data Source=.;initial catalog=MyDbName;integrated security=True;");

         try
         {
            // Try to delete the snapshot in case it was left over from aborted test runs
            DatabaseSnapshot.DeleteSnapShot();
         }
         catch { /* this should fail with snapshot does not exist */ }

         DatabaseSnapshot.CreateSnapShot();
      }

      [TearDown]
      public void FixtureTearDown()
      {
         DatabaseSnapshot.DeleteSnapShot();
      }
   }
}
TestBase.cs

All of our test classes are made to inherit from this class:

using NUnit.Framework;

namespace Testing.Shared
{
   public class TestBase
   {
      [TearDown]
      public void TearDown()
      {
         SetupAndTeardown.DatabaseSnapshot.RestoreSnapShot();
      }
   }
}

Which restores the database back to the snapshot position at the end of each test. And that... Is that!