Thursday, December 1, 2016

Testing a Request in an ApiController

I recently came across a situation where I needed to test an action method on an ApiController to make sure the correct response was returned to the user based on the request.  In this particular case I was testing the ability to upload a file to an API and I needed to return a 400 (Bad Request) error if the content type of the request was not right.  I knew the code was working (I know, it wasn't TDD, but sometimes you have to roll with the punches), but I was having a hard time testing it.  To make things worse I couldn't use shims or fakes because the build server kept blowing up on them.  Fortunately, I came across a couple of really helpful blogs that pointed me in the right direction.

First, Shiju Varghese's blog on writing unit tests for an ApiController.  Next I used William Hallat's blog on testing a file upload to finish things off.

What I ended up with is pretty neat and easy to use, customized to meet my needs.  As usual, I'm posting it here so I don't have to redo the work next time.

The first key to making this work is declaring the controller.  Let's just say that our controller has a single object injected, an ILogger (a fairly common practice).  Instead of just doing this:
var controller = new ExampleController(_moqLogger.Object);

We want to do this:
   1:  var controller = new ExampleController(_moqLogger.Object)
   2:  {
   3:      Request = new HttpRequestMessage
   4:      {
   5:          Content = new ObjectContent(typeof(string), null, new JsonMediaTypeFormatter()),
   6:          Method = HttpMethod.Post
   7:      }
   8:  };

UPDATE: We actually also want to include an HttpConfiguration to prevent another error I was getting later:
   1:  var request = new HttpRequestMessage
   2:  {
   3:      Content = new ObjectContent(typeof(string), null, new JsonMediaTypeFormatter()),
   4:      Method = HttpMethod.Post
   5:  };
   6:  request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration());
   7:  var controller = new ExampleController(_moqLogger.Object)
   8:  {
   9:      Request = request
   8:  };

This declares that the request passed to the controller will actually be specified to contain content and a method (POST in this case).  Now that we have that, our tests won't fail with the awful (and unhelpful) "Object reference not set to an instance of an object" error you might be seeing.

In this case we've specified that the content will be a string, but we haven't specified any actual content.  But as I said before I needed to test whether the content was a file that had been uploaded, then also take certain actions based on that file.  To do that I actually needed to fake a file upload in the request itself.

A little bit of setup (this happens before each test run not each test):
   1:  [TestFixtureSetUp]
   2:  public void SetUpFixture()
   3:  {
   4:      using (var outFile = new StreamWriter(_testFile))
   5:      {
   6:          outFile.WriteLine("some test data");
   7:      }
   8:  }

The test:
   1:  [Test]
   2:  public void DoSomethingShouldReturnAnOkResult()
   3:  {
   4:      // arrange
   5:      var multipartContent = BuildFormDataContent();
   6:      multipartContent.Add(new StringContent("some value"), "someKey");
   7:   
   8:      var controller = new ExampleController(_moqLogger.Object)
   9:      {
  10:          Request = new HttpRequestMessage
  11:          {
  12:              Content = multipartContent,
  13:              Method = HttpMethod.Post
  14:          }
  15:      };
  16:   
  17:      // act
  18:      var response = controller.DoSomething();
  19:   
  20:      // assert
  21:      Assert.IsInstanceOf<OkResult>(response.Result);
  22:  }

The method called by the test:
   1:  private string _testFile = "test.file";
   2:   
   3:  private MultipartFormDataContent BuildFormDataContent()
   4:  {
   5:      var multipartContent = new MultipartFormDataContent("boundary=---011000010111000001101001");
   6:              
   7:      var fileStream = new FileStream(_testFile, FileMode.Open, FileAccess.Read);
   8:      var streamContent = new StreamContent(fileStream);
   9:      streamContent.Headers.ContentType = new MediaTypeHeaderValue("multipart/form-data");
  10:   
  11:      multipartContent.Add(streamContent, "TheFormDataKeyForTheFile", _testFile);
  12:   
  13:      return multipartContent;
  14:  }

And finally, the controller action method:
   1:  [HttpPost]
   2:  public async Task<IHttpActionResult> DoSomething()
   3:  {
   4:      if (!Request.Content.IsMimeMultipartContent("form-data"))
   5:      {
   6:          _logger.Information(() => "Unsupported media type");
   7:          return BadRequest("Unsupported media type");
   8:      }
   9:   
  10:      try
  11:      {
  12:          var root = @"C:\";
  13:          var provider = new MultipartFormDataStreamProvider(root);
  14:          await Request.Content.ReadAsMultipartAsync(provider);
  15:   
  16:          var someValue = provider.FormData.GetValues("someKey").FirstOrDefault();
  17:   
  18:          foreach (var file in provider.FileData)
  19:          {
  20:              var fileInfo = new FileInfo(file.LocalFileName);
  21:              // do something with the file here that returns a boolean
  22:              if(someOtherMethod()){
  23:                  return Ok();
  24:              }            
  25:   
  26:              return InternalServerError(new Exception("An error was encountered while processing the request"));
  27:          }
  28:   
  29:          return Ok();
  30:      }
  31:      catch (Exception ex)
  32:      {
  33:          return InternalServerError(ex);
  34:      }
  35:  }

This solved my problem and enabled me to test my action method on my controller.

Friday, November 18, 2016

Testing Exception Messages with MS Test

I usually use NUnit to do my C# unit tests, but when I need to use the Microsoft Fakes framework I have to use the built-in (to Visual Studio) MS Test framework.  This is because Fakes requires instrumentation ability that NUnit just doesn't have.  It also means I can't use the Resharper test runner like I normally do.  Neither of those changes is usually a problem, but today I found out that testing an exception message in MS Test is actually a bit tricky.

You can use the
[ExpectedException]
attribute around a test to indicate to MS Test that you expect that test to throw an exception. However, even though there is an overload for that attribute that accepts a message, it's not actually used to test whether the thrown exception has the message you specify in the attribute. So
[ExpectedException(typeof(ArgumentNullException), "A name is required")]
doesn't actually check whether the message on the exception is
"A name is required"
. Instead, if that test fails (which would happen if an exception wasn't thrown since you're expecting that one will be thrown) the text that will appear in Test Explorer is
"A name is required"
. The good news is that there's an easy way around this. The bad news is you still have to create your own attribute to do it. It's not complicated. h/t to BlackjacketMack on SO for posting his solution here.

   1:  public class ExpectedExceptionWithMessageAttribute : ExpectedExceptionBaseAttribute
   2:  {
   3:      public Type ExceptionType { get; set; }
   4:   
   5:      public string ExpectedMessage { get; set; }
   6:   
   7:      public ExpectedExceptionWithMessageAttribute(Type exceptionType)
   8:      {
   9:          ExceptionType = exceptionType;
  10:      }
  11:   
  12:      public ExpectedExceptionWithMessageAttribute(Type exceptionType, string message)
  13:      {
  14:          ExceptionType = exceptionType;
  15:          ExpectedMessage = message;
  16:      }
  17:   
  18:      protected override void Verify(Exception exception)
  19:      {
  20:          if (exception.GetType() != ExceptionType)
  21:          {
  22:              NUnit.Framework.Assert.Fail(
  23:                  "ExpectedExceptionWithMessageAttribute failed. Expected exception type: {0}. Actual exception type: {1}. Exception message: {2}",
  24:                  ExceptionType.FullName, exception.GetType().FullName, exception.Message);
  25:          }
  26:   
  27:          var actualMessage = exception.Message.Trim();
  28:   
  29:          if (ExpectedMessage != null)
  30:          {
  31:              Assert.AreEqual(ExpectedMessage, actualMessage);
  32:          }
  33:      }
  34:  }

You use it just like you used
[ExpectedException]
earlier.
[ExpectedExceptionWithMessage(typeof(ArgumentNullException), "A name is required")]

More Fun with Shims

I'm finally back to doing some server side code (as opposed to the client stuff I've been working exclusively with for several months) and I found myself in need of some unit tests.  It's been a long time since I used a shim from Microsoft's Fakes framework so I had to poke and prod it for a while to work and I don't want to forget what I did.  Here goes:

In this particular example I was working with the WSUS API provided by Microsoft to interact with WSUS.  I specifically was trying to save a new signing certificate, but I obviously didn't want to actually do that on a WSUS server.  The solution was to use shims and stubs this time.

   1:  private string _fileNameParameter = "empty";
   2:  private SecureString _passwordParameter = new SecureString();

   1:  private StubIUpdateServer PrepareIUpdateServerStub()
   2:  {
   3:      FakesDelegates.Action<string, SecureString> setSigningCertificateAction = (fileName, password) =>
   4:      {
   5:          _fileNameParameter = fileName;
   6:          _passwordParameter = password;
   7:      };
   8:   
   9:      var iUpdateServerConfiguration = new StubIUpdateServerConfiguration
  10:      {
  11:          SetSigningCertificateStringSecureString = setSigningCertificateAction
  12:      };
  13:   
  14:      var iUpdateServerStub = new StubIUpdateServer
  15:      {
  16:          GetConfiguration = () => iUpdateServerConfiguration
  17:      };
  18:   
  19:      return iUpdateServerStub;
  20:  }

   1:  [TestMethod]
   2:  public void SetSigningCertificateShouldPassParametersToWsusApiAndReturnTrue()
   3:  {
   4:      using (ShimsContext.Create())
   5:      {
   6:          // arrange
   7:          const string expectedFileName = "fileName";
   8:          const string expectedPasswordString = "password";
   9:          var expectedPassword = new SecureString();
  10:          foreach (var c in expectedPasswordString)
  11:          {
  12:              expectedPassword.AppendChar(c);
  13:          }
  14:   
  15:          ShimAdminProxy.GetUpdateServerStringBooleanInt32 = (name, isSsl, port) => PrepareIUpdateServerStub();
  16:   
  17:          var wsusRepository = new WsusRepository();
  18:   
  19:          // act
  20:          var result = wsusRepository.SetSigningCertificate(expectedFileName, expectedPassword);
  21:   
  22:          // assert
  23:          Assert.AreEqual(expectedFileName, _fileNameParameter);
  24:          Assert.AreEqual(expectedPassword.ToString(), _passwordParameter.ToString());
  25:          Assert.IsTrue(result);
  26:      }
  27:  }

I broke out a bunch of the stub creation stuff so I could reuse it across multiple tests.  That's what the PrepareIUpdateServerStub method is for.  That's also why I have the global variables _fileNameParameter and _passwordParameter.  The method I was testing is essentially a pass-through to the WSUS API that we reuse in multiple applications so the method itself was fairly straightforward (check if a file and password were passed, then pass them on to WSUS).

Like I said at the beginning, working with Shims, Fakes, and Stubs is always a challenge for me so hopefully this documentation will help me next time I need to do it.

Tuesday, November 15, 2016

Local NPM Package

If you find yourself writing an NPM package, but you don't want to release it before you test it, there's a neat way to install a package from your local machine to another project.  You can use npm link instead of npm install.

Go to the directory containing the package you're writing (that you want to test by referencing in another project) and make sure to do an npm init on that directory (this creates the package.json file used by npm).  Once you're done run npm link.  Now go to the project you want to reference the package in and run npm link and pass it the name of the package you used during npm init.

That's it!

Thursday, October 20, 2016

TemplateUrl Function for Dynamic Templating

I've come across multiple situations while using Angular JS where I'd like to dynamically load a template into a directive.  It used to be pretty difficult, but in more recent versions (I'm not sure when it was introduced) you can pass a function to templateUrl to somewhat dynamically determine which template you want to use.  Most recently this came in handy when I was nesting a bunch of tables within each other (yes, it was a valid use case), but I also wanted those tables to be available alone.

I made a template for each table - 5 total - and then passed the relative url of the template I wanted as an attribute to the directive.  So it worked out like this:

<ea-children details="childrenFirst" template-url="/templates/first.html"></ea-children>
<ea-children details="childrenSecond" template-url="/templates/second.html"></ea-children>
<ea-children details="childrenThird" template-url="/templates/third.html"></ea-children>

As you can see I was able to reuse the same directive three times with potentially drastically different markup.  The templateUrl function looks like this:

templateUrl: function (element, attributes) {
    return attributes.templateUrl;
}

One major item to note is that you don't have access to scope variables because the template is requested before scope is initialized.

Hopefully next time I need to do this (assuming I'm still using Angular 1.x) I'll remember to check here first.  Happy coding!

Tuesday, October 11, 2016

Angular Tree View

For the second (or maybe third) time I faced a situation that would drastically benefit from a Tree View control.  Since I love Angular and was using it anyway, I finally set out to create a Tree View control I could reuse and be proud of.  It's pretty basic, but that's kinda the point.  There's actually a little bit of styling on this one, too.  Once again I'm going to try to get it up on Github at some point, but for now you can check it out on plnkr.

UPDATE: I've changed the directive significantly, checked it into Github and also created an npm package out of it called angular-tree-view.  I'm still updating a lot of it, but it's alive and in the wild now!

Check it out on Github
Check it out on npm

An important piece to note is that this feature actually uses two different directives nested within each other.  There's the parent item, which is a tree-view and then there's the child item, which is a tree-view-item.  Each tree-view-item can have another tree-view in it, which can then have another tree-view-item, which can then have another tree-view, etc.

Directive Requirements:
  • Each item can have an unlimited number of sub-items
  • Each sub-item can have an unlimited number of sub-items (ad infinitum)
  • The user can specify a callback to be invoked when a sub-item with no sub-items of its own is clicked
  • The selected item should be indicated by the application of an "active-branch" class
Directive Module Code:
angular.module('ea.treeview', []).directive('eaTreeView', function() {
  return {
        restrict: 'E',
        scope: {
            items: '=',
            isRoot: '@',
            callback: '&'
        },
        link: function(scope) {
            scope.callback = scope.callback();
        },
        controller: function ($scope) {
            this.isRoot = $scope.isRoot;
        },
        template: '<ea-tree-view-item item="item" callback="callback" data-ng-repeat="item in items"></ea-tree-view-item>'
    }
}).directive('eaTreeViewItem', function() {
  return {
        restrict: 'E',
        scope: {
            item: '=',
            callback: '&'
        },
        require: '^eaTreeView',
        link: function (scope, element, attributes, controller) {
          scope.callback = scope.callback();
            scope.activate = function () {
              var activeElements = document.getElementsByClassName('active-branch');
              angular.forEach(activeElements, function(activeElement){
                angular.element(activeElement).removeClass('active-branch');
              });
              element.children('div').addClass('active-branch');
              scope.callback(scope.item);
            };

            scope.hasChildren = function () {
                return !!scope.item.items && !!scope.item.items.length;
            };

            scope.hasParent = function() {
                return !controller.isRoot;
            };

            scope.toggleExpanded = function() {
                scope.item.expanded = !scope.item.expanded;
            };
        },
        templateUrl: 'treeViewItem.html'
    }
});

The treeViewItem Template:
<div>
    <div data-ng-if="hasChildren()" class="tree-parent" data-ng-class="{'tree-child': hasParent()}">
        <div data-ng-click="toggleExpanded()" class="clickable">
            <i class="fa" data-ng-class="{'fa-chevron-right': !item.expanded, 'fa-chevron-down': item.expanded}"></i>
            <span>{{item.display}}</span>
        </div>
        <ea-tree-view data-ng-if="item.expanded" callback="callback" items="item.items"></ea-tree-view>
    </div>
    <div data-ng-if="!hasChildren()" data-ng-click="activate()" class="tree-child clickable">{{item.display}}</div>
</div>

A dash of styling:
.clickable {
  cursor: pointer;
}

.active-branch {
    background-color: lightgrey;
}

.tree-parent {
  padding-left: 0;
}

.tree-child {
  padding-left: 1em;
}

.tree-final-child {
  padding-left: 2em;
}

And finally some usage:
<ea-tree-view items="model.items" is-root="true" callback="go"></ea-tree-view>

angular.module('demo', ['ea.treeview']).controller('mainController', function($scope) {
  $scope.model = {
    items: [
      {
        display: 'Abe',
        items: [
          {display: 'Homer', items: [{display: 'Bart'}, {display: 'Lisa'}, {display: 'Maggie'}]},
          {display: 'Herb'},
          {display: 'Abbie'}
        ]
      },
      {
        display: 'Jacqueline',
        items: [
          {display: 'Patty'},
          {display: 'Selma'},
          {display: 'Marge', items: [{display: 'Bart'}, {display: 'Lisa'}, {display: 'Maggie'}]}
        ]
      }
    ]
  };
  
  $scope.go = function (item) {
      console.log("You could have navigated!", item);
  };
});

Angular Tri-State Loading Directive

I had a situation where I wanted to load some data asynchronously and display a visual cue to the user that the action had been completed.  However, there was a possibility that more than one result could be returned and the user would have to take some action to confirm which item they want to use.  I also wanted to show the user a "loading" message so they'd have the visual cue that something was happening.  Since I was already using Angular and Font Awesome I decided the best way to go about it would be to use a directive.  I prefer my directives to be configurable as much as possible so I can reuse them in the future with relative ease (that is the point of directives after all).  This is what I came up with.  I will at some point get it uploaded to Github, but for now this is the code.  (You can check it out on plnkr, too).

First I had to create the directive.  I've refactored it a little bit so that it's usable as a separate module by just including 'ea.loading' in your module import list when you declare your module.

Directive Requirements:

  • User can configure which icons to use, but defaults will be used if nothing is specified
  • User can configure which color(s) to use on which states and no defaults will be used
  • User can specify a callback function and parameters for each of the three states separately (for click events only)
  • User can specify an initial state, but a default of 0 (do not show) will be used if nothing is specified

Directive Module Code:
angular.module('ea.loading', []).directive('eaTriStateLoading', function() {
  return {
    restrict: 'E',
    scope: {
      animate: '@',
      initialValue: '@',
      loadingCallback: '&',
      loadingCallbackParameters: '=',
      loadingClass: '@',
      loadingColor: '@',
      notOkCallback: '&',
      notOkClass: '@',
      notOkColor: '@',
      okCallback: '&',
      okClass: '@',
      okColor: '@',
      state: '='
    },
    link: {
      pre: function(scope, element, attributes) {
        scope.animate = scope.animate || true;
        scope.state = parseInt(scope.initialValue) || 0;
        scope.loadingClass = scope.loadingClass || 'fa-spinner';
        scope.okClass = scope.OkClass || 'fa-check';
        scope.notOkClass = scope.notOkClass || 'fa-exclamation-triangle';
      },
      post: function(scope, element, attributes) {
        function buildParameterObject(parameters) {
          var result = {};
          
          for (var i = parameters.length; --i >= 0;) {
            var parameter = parameters[i];
            for (var j = Object.keys(parameter).length; --j >= 0;) {
              // key is the name of the parameter
              // parameter[key] is the name of the property on scope that will contain the value to be passed to the parameter
              var key = Object.keys(parameter)[j];
              var value = scope.$parent[parameter[key]] || parameter[key].substring(1, parameter[key].length - 1);
              result[key] = value;
            }
          }
          
          return result;
        }
        
        scope.click = function($event) {
          var parameter = {};
          if (scope.state === 1 && !!scope.loadingCallback && angular.isDefined(scope.loadingCallback) && angular.isFunction(scope.loadingCallback)) {
            if (!!scope.loadingCallbackParameters) {
              parameter = buildParameterObject(scope.loadingCallbackParameters);
            }
            parameter.$event = $event;
            scope.loadingCallback(parameter);
          } else if (scope.state === 2 && angular.isDefined(scope.okCallback) && angular.isFunction(scope.okCallback)) {
            if (!!scope.okCallbackParameters) {
              parameter = buildParameterObject(scope.okCallbackParameters);
            }
            parameter.$event = $event;
          } else if (scope.state === 3 && angular.isDefined(scope.noOkCallback) && angular.isFunction(scope.noOkCallback)) {
            if (!!scope.notOkCallbackParameters) {
              parameter = buildParameterObject(scope.notOkCallbackParameters);
            }
            parameter.$event = $event;
            scope.notOkCallback(parameter);
          }
        };
      },
    },
    template: '<i class="fa" ng-class="{\'{{loadingClass}}\': state === 1, \'fa-pulse\': state === 1 && animate === true, \'{{okClass}}\': state === 2, \'{{notOkClass}}\': state === 3}" ng-click="click($event)" ng-style="{color: (state === 1 ? loadingColor : state === 2 ? okColor : notOkColor)}"></i>'
  }
});

And some use cases:
<ea-tri-state-loading loading-callback-parameters="model.firstDemoLoadingCallbackParameters" loading-callback="clickCallback(property, model.age, $event)" loading-color="blue" not-ok-color="red" ok-callback="decrement()" ok-color="green" state="model.firstDemoValue"></ea-tri-state-loading>
<ea-tri-state-loading loading-class="fa-circle-o-notch" loading-callback-parameters="[{'property': '\'secondDemoValue\''}]" loading-callback="clickCallback(property, $event)" loading-color="purple" not-ok-color="yellow" ok-color="orange" initial-value="2" state="model.secondDemoValue"></ea-tri-state-loading>
<ea-tri-state-loading animate="false" ok-class="fa-rocket" state="model.thirdDemoValue"></ea-tri-state-loading>
<ea-tri-state-loading not-ok-class="fa-remove" state="model.fourthDemoValue"></ea-tri-state-loading>
<ea-tri-state-loading state="model.fifthDemoValue"></ea-tri-state-loading>
And finally the controller that would back those use cases:
angular.module('demo', ['ea.loading']).controller('mainController', function($scope) {
  $scope.states = {
    first: 1,
    second: 2,
    third: 3
  };
  $scope.model = {
    firstDemoValue: 1,
    secondDemoValue: 1,
    thirdDemoValue: 1,
    fourthDemoValue: 2,
    fifthDemoValue: 3,
    age: 12,
    firstDemoLoadingCallbackParameters: [
      {'property': '"firstDemoValue"'},
      {'value': 'age'}
    ]
  };
  
  $scope.clickCallback = function(property, value, $event) {
    console.log('property', property);
    console.log('value', value);
    console.log('$event', $event);
  };
  
  $scope.simulateLoading = function(property) {
    $scope.model[property] = 1;
  }
  
  $scope.simulateOk = function(property) {
    $scope.model[property] = 2;
  };
  
  $scope.simulateNotOk = function(property) {
    $scope.model[property] = 3;
  };
});