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!