Using Microsoft.AspNet.Server.WebListener to host integration tests – ASP.NET Core

Reason

When integration testing a web solution it’s awesome to have a light weight hosting option like WebListener to quickly spin up a web server and dispose it again without the need to set up dedicated IIS sites.

In a nutshell, the following steps are required to run automated tests against a site hosted in a WebListener:

  1. Start it.
  2. Run tests.
  3. Stop it.

Step 1 & 2 are easy enough, but since WebListener waits for manual input to stop, step 3 currently requires some creativity.

"Application started. Press Ctrl+C to shut down."

“Application started. Press Ctrl+C to shut down.”

Code

In the following code sample Gulp and PowerShell are used to spawn a WebListener process, get the process ID, run arbitrary tasks (e.g. integration tests) and then stop the process based on the process ID from step 1.

The majority of the work is done by PowerShell:

  • Start via a DNX command (“web” has been included in most ASP.NET vNext samples I’ve seen):

    $process = Start-Process 'dnx' 'web' -WorkingDirectory '(YourWorkingDirectory)' -PassThru; 
    $process.Id;
    
  • Start using explicit argument list:
    $process = Start-Process 'dnx' -ArgumentList 'Microsoft.AspNet.Hosting', '--server Microsoft.AspNet.Server.WebListener', '--server.urls http://localhost:5000' -WorkingDirectory '(YourWorkingDirectory)' -PassThru; 
    $process.Id;
    
  • Stop-Process $process.Id;” should be self explanatory.

The following snippet is an example of how the three steps can be tied together via a Gulp task.

var gulp = require("gulp");
var serverProcessId;

// Entry point
gulp.task("test", ["test-stop-server"], function () {
    console.log("Done!");
});

// Step 3
gulp.task("test-stop-server", ["test-run-tests"], function () {
    childProcess.run("PowerShell.exe", "Stop-Process " + serverProcessId);
    console.log("Server stopped. Process ID %s.", serverProcessId);
});

// Step 2
gulp.task("test-run-tests", ["test-start-server"], function () {
    console.log("Do stuff here.");
});

// Step 1
gulp.task("test-start-server", function (callback) {
    var workingDirectory = require("path").join(__dirname, "Your/Working/Directory");
    var startServerScript = "$process = Start-Process 'dnx' 'web' -WorkingDirectory '" + workingDirectory + "' -PassThru; $process.Id;";
    childProcess.run("PowerShell.exe", startServerScript, setServerProcessId);

    function setServerProcessId(data) {
        var processId = parseInt(data.toString());
        if (isFinite(processId)) {
            serverProcessId = processId;
            console.log("Server started. Process ID %s.", serverProcessId);
        } else {
            console.log("Potential error starting test server:\n%s", data.toString());
        }
        callback();
    };
});

// Helper object to run PowerShell via Node.js.
var childProcess = {
    run: function(executable, args, stdout, stderr) {
        console.log("Running \"%s %s\".", executable, args);

        var spawn = require("child_process").spawn;
        var child = spawn(executable, [args]);

        child.stdout.on("data", function(data) {
            if (stdout) {
                stdout(data);
            } else {
                console.log(data.toString());
            }
        });

        child.stderr.on("data", function(data) {
            if (stderr) {
                stderr(data);
            } else {
                console.log(data.toString());
            }
        });

        child.on("exit", function(code, signal) {
            console.log("Done running \"%s %s\". Child process terminated with code %s.", executable, args, code);
        });

        child.stdin.end();
    }
};

Example

Sample output from Task Runner Explorer.

Sample output from Task Runner Explorer.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s