Saturday, February 13, 2016

Unit Testing Plain Old JavaScript (Part 2)

At this point we have our test runner all setup and we're ready to start writing our tests (remember that we're doing this using test-driven development).  If you're confused, check out part 1 to see how we got here.  First things first, let's write a test (spec if you're sticking with Jasmine's nomenclature):

describe('Hangman', function() {
    it('should populate alphabet with 26 characters', function() {
        buildAlphabetArray();
        
        expect(alphabet.length).toBe(26);
    });
});

After creating that if we refresh the SpecRunner.html we should see that we have an error that says buildAlphabetArray is not defined.  That's good.  That's our red (in the red-green-refactor process of TDD).  Let's fix it by creating an empty function named buildAlphabetArray (in our src/hangman.js file):

function buildAlphabetArray() {
}

Now we have a new error that says alphabet is not defined.  Because it isn't.  Let's fix that.

var alphabet;

function buildAlphabetArray() {
}

Save and refresh and we get a new error that it cannot read property length of undefined.  We've declared alphabet, but it isn't defined, initialized, or populated.  Let's fix that.

var alphabet = [];

function buildAlphabetArray() {
}

Now we finally have what we're expecting: an error that says we expected 0 to be 26.  That's because we never did populate that array.  We'll do that now.

var alphabet = [];

function buildAlphabetArray() {
    alphabet = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
}

And now our test passes.  Make sure to check out the exciting conclusion!

Unit Testing Plain Old JavaScript (Part 1)

I've written about unit testing quite a bit, with a focus on Jasmine (for Angular) and NUnit (for .NET).  Lately I've been interested in improving my skills as a front-end developer so I started thinking about writing a simple HTML5 based Hangman game for my kids to use to practice their vocabulary.  One thing led to another and I ended up deciding that I wanted to do the whole thing as simply as possible with a full complement of unit tests.  Well, to do that I need to be able to unit test plain old JavaScript, and here we are.

The first thing to do is download the Jasmine framework.  I've created a folder on my desktop called Hangman to hold everything.  So I went here and downloaded the latest version of Jasmine (2.4.1 as of this writing).  I extracted the files from the /lib/jasmine-2.4.1 folder into a /lib folder in my Hangman folder.  You can technically get by with just jasmine.js, jasmine.html.js, and boot.js, but I use all the files because the styling is nice and they're already done anyway so it doesn't hurt.

Really briefly, jasmine.js contains the actual jasmine testing framework.  jasmine-html.js contains functions that help format the page (and do some other things that you can see for yourself if you look at the file).  boot.js initializes jasmine.

Now we need code to test.  In my Hangman folder I have a folder called src and a folder called spec.  In the src folder I have a single file: hangman.js.  In the spec folder I have a single file: hangman.js (I usually prefer to name my spec files with either a .test.js or .spec.js suffix, but I didn't this time).  Since I'm using test-driven development neither file contains anything right now.

The last piece is SpecRunner.html.  This is the file that you'll open to run your tests.  In order to run your tests, this file will need to know where your tests and your code are.  So the contents of SpecRunner.html look pretty much like this:

<html>
<head>
  <title>Jasmine Spec Runner v2.4.1</title>

  <link href="lib/jasmine_favicon.png" rel="shortcut icon" type="image/png"></link>
  <link href="lib/jasmine.css" rel="stylesheet"></link>

  <script src="lib/jasmine.js"></script>
  <script src="lib/jasmine-html.js"></script>
  <script src="lib/boot.js"></script>

  <!-- include source files here... -->
  <script src="src/hangman.js"></script>

  <!-- include spec files here... -->
  <script src="spec/hangman.js"></script>

</head>

<body>
</body>
</html>

If you open that HTML file, you'll see something like this:





That's the end of this section.  Check out part two once you have a grasp on this!

Friday, February 5, 2016

NUnit TestCase Attribute

The other day I showed how to use NUnit's TestCaseSource attribute to use a single test to run multiple cases.  Unfortunately, in my haste to get that post up I used a really bad example for it.  Let me refresh your memory.  Imagine we have a method that accepts three strings and returns one:

   1:  public string GetFullName(string firstName, string lastName, string middleName)
   2:  {
   3:      throw new NotImplementedException();
   4:  }

There's a really easy way to pass multiple cases to this method without using TestCaseSource.  You can just use the TestCase attribute.  If our test looks like this:

   1:  [Test]
   2:  public void GetFullName_ShouldConcatenateFirstMiddleAndLastWhenAllThreeHaveValues()
   3:  {
   4:      // arrange
   5:      var program = new Program();
   6:   
   7:      // act
   8:      var fullName = program.GetFullName("Jumping", "Flash", "Jack");
   9:   
  10:      // assert
  11:      Assert.AreEqual("Jumping Jack Flash", fullName);
  12:  }

We can change it to look like this, and it will run two separate tests:

   1:  [Test]
   2:  [TestCase("Jumping", "Flash", "Jack", "Jumping Jack Flash", "ShouldConcatenateFirstMiddleAndLastWhenAllThreeHaveValues")]
   3:  [TestCase("", "Flash", "Jack", "Jack Flash", "ShouldConcatenateMiddleAndLastWhenFirstIsEmptyString")]
   4:  public void GetFullName_ShouldMapNameCorrectly(string firstName, string lastName, string middleName, string expectation, string errorMessage)
   5:  {
   6:      // arrange
   7:      var program = new Program();
   8:   
   9:      // act
  10:      var fullName = program.GetFullName(firstName, lastName, middleName);
  11:   
  12:      // assert
  13:      Assert.AreEqual(expectation, fullName, errorMessage);
  14:  }

This is effectively the same thing as we saw the other day, but when you have simple types as all of your parameters, this is easier.  TestCaseSource really comes into play when you want to pass in a complex object as a parameter.  I'll try to post an example of that soon.

Thursday, February 4, 2016

SQL WHERE Clause Abnormality

I've been working with SQL Server for a long time (about 10 years) and I recently found out that in all that time I completely misunderstood something about WHERE clauses.  I've always written my WHERE clauses with a healthy dose of parentheses in order to logically separate the conditions, particularly when an OR condition was involved joining multiple sets of complex AND connected conditions.  Apparently you don't have to do that.  Check out the example below to see what I mean.

CREATE TABLE #temp (
     ID INT
    ,StartDate DATETIME2
    ,EndDate DATETIME2
)

DECLARE @Today DATE = CONVERT(DATEGETDATE())
DECLARE @Tomorrow DATE = CONVERT(DATEDATEADD(dd, 1, @Today))

INSERT INTO #temp (ID, StartDate, EndDate)
VALUES (1, @Today, @Tomorrow), (2, DATEADD(dd, -2, @Today), @Today), (3, '01/01/2014''12/31/2015'), (4, @Today, NULL), (4, @Today, '12/31/2016')

SELECT *
FROM #temp
WHERE
    StartDate >= @Today
    AND EndDate <= @Tomorrow
    OR ID = 4
    AND (
        StartDate >= @Today
        AND EndDate IS NULL
    )


Prior to this realization I thought the above query would pull back the following:

  • Any records with a StartDate in the future and an EndDate in the past
  • Any records with a StartDate in the future and a NULL EndDate
  • Any records with an ID of 4
What I've found, though, is that the OR causes a switch to happen so that only the following are pulled back:
  • Any records with a StartDate in the future and an EndDate in the past
  • Any records with an ID of 4 and a StartDate in the future and a NULL EndDate
I still don't like the way it reads.  I'd still rather see it written like this:

SELECT *
FROM #temp
WHERE
    (
        StartDate >= @Today
        AND EndDate <= @Tomorrow
    ) OR (
        ID = 4
        AND StartDate >= @Today
        AND EndDate IS NULL
    )


But it is good to know the other way is legal.

Friday, January 29, 2016

NUnit TestCaseSource Attribute

As you've probably picked up if you've read my other posts (I'm pretty sure I said it outright), I'm a huge fan of automated unit testing.  I've used MS Test and NUnit to test my C# code and I used to think they were pretty much equal.  However, I found something in NUnit recently that has me leaning their way.  To be fair, I'm not sure whether this is possible with MS Test and I have no reason to find out (we're using NUnit at the client).

I tend to write long test names.  Like, really long.  I tell people that I prefer clarity in the name over brevity.  I've also recently been converted to the single test philosophy where a single unit test only checks one thing.  That means that if you have a complex object of a Person you'd have one test for FirstName and another test for LastName, for example.  As you can probably imagine, my unit test files were pretty big.  That has a lot of negative ramifications, not the least of which is that it's hard to find whether you've already tested something.

UPDATE: This isn't the best example.  Check out this other post for a simpler way to do this.

Enter NUnit's TestCaseSourceAttribute and iterators.  What you can do is write a single test that accepts parameters, then create an iterator-based property that runs that test with different parameters.

Let's say we have a method called GetFullName, which takes a first, middle, and last name and concatenates them to make a full name:
   1:  public string GetFullName(string firstName, string lastName, string middleName)
   2:  {
   3:      throw new NotImplementedException();
   4:  }

So we write a basic test to confirm that when we pass all three parameters we get them back as "[first] [middle] [last]".  We'll call it "GetFullName_ShouldConcatenateFirstMiddleAndLastWhenAllThreeHaveValues":
   1:  [Test]
   2:  public void GetFullName_ShouldConcatenateFirstMiddleAndLastWhenAllThreeHaveValues()
   3:  {
   4:      // arrange
   5:      var program = new Program();
   6:   
   7:      // act
   8:      var fullName = program.GetFullName("Jumping", "Flash", "Jack");
   9:   
  10:      // assert
  11:      Assert.AreEqual("Jumping Jack Flash", fullName);
  12:  }

We update the method so the test passes:
   1:  public string GetFullName(string firstName, string lastName, string middleName)
   2:  {
   3:      return string.Format("{0} {1} {2}", firstName, middleName, lastName);
   4:  }

So now we have a problem.  Our test passes, but it's only the positive test case.  This method will clearly only work the way we want if we pass all three names.  If first name is null or empty we'll end up with a leading space we don't want.  We can create more tests to handle null values and empty strings, but that's a lot of combinations of values for a really simple method.  What we can do instead is use TestCaseSource and iterators.  Here's how we'd rewrite that one test this new way:
   1:  public static IEnumerable GetFullNameTestsCases
   2:  {
   3:      get
   4:      {
   5:          yield return
   6:              new TestCaseData("Jumping", "Flash", "Jack", "Jumping Jack Flash").SetName(
   7:                  "ShouldConcatenateFirstMiddleAndLastWhenAllThreeHaveValues");
   8:      }
   9:  }
  10:   
  11:  [Test, TestCaseSource("GetFullNameTestsCases")]
  12:  public void GetFullName(string firstName, string lastName, string middleName, string expectation)
  13:  {
  14:      // arrange
  15:      var program = new Program();
  16:   
  17:      // act
  18:      var fullName = program.GetFullName(firstName, lastName, middleName);
  19:   
  20:      // assert
  21:      Assert.AreEqual(expectation, fullName);
  22:  }

What we're doing here is creating the test data in the GetFullNameTestCases property, then we're specifying where the test should get its inputs by using the TestCaseSource attribute on the test itself.  As long as the number of parameters matches the number of arguments, everything will work fine.  Furthermore, we can add additional tests really easily just by adding more yield return statements.  Let's say we wanted to test for a null first name.  We just add this to the property:
   1:  yield return
   2:      new TestCaseData(null, "Flash", "Jack", "Jack Flash").SetName(
   3:          "ShouldConcatenateFirstMiddleAndLastWhenAllThreeHaveValues");

Next time we run our test, both sets of test data will get picked up and passed separately to the test method.  This method can greatly speed up your test writing and make it easier to cover more cases.

Mocking the Database

I believe I mentioned before that I worked with a custom, home-grown ORM at one of my clients.  If I didn't mention that, I'm mentioning it now.  One of the biggest problems I had with the solution we implemented was that we didn't have any tests for it.  Every now and then we'd encounter a new scenario (like returning a list of objects instead of a single object) and we'd have to code it in, with no way to make sure we weren't breaking what was already there.

One (particularly slow) day I decided enough was enough and I set out to create automated unit tests for every method in that behemoth.  That's when I encountered the problem: how do you mock a database for consumption by ADO.NET?  It turns out it's pretty easy and pretty straightforward.  First off, I found this code somewhere else (which, I know, technically violates the name of the blog, but it is what it is).  You can check out the original post here if you're interested.  I had to make a few changes for my version of MOQ and my specific circumstances, but that's the blog that got me started down the right path.

Here's the high level overview of the ORM: go get data using a DbDataReader, read through the columns in the reader, and for each column, find a corresponding property on the object and map the value from the reader to the property.  It's honestly pretty straightforward.  All I had to figure out was how to mock a DbDataReader.  Here it is:


   1:  private Mock<DbDataReader> CreateFakeDbDataReader(int numberOfFields, int numberOfReads = 1,
   2:              MockBehavior mockBehavior = MockBehavior.Loose, bool hasRows = true)
   3:  {
   4:      // create a mock repository (database)
   5:      var repository = new MockRepository(mockBehavior);
   6:      // create a reader for the mocked repository
   7:      var moqReader = repository.Create<DbDataReader>();
   8:      // setup the reader so that it always has rows (or indicates that it has rows anyway)
   9:      moqReader.SetupGet(p => p.HasRows).Returns(hasRows);
  10:      // setup the reader to indicate it has the specified number of fields
  11:      moqReader.SetupGet(p => p.FieldCount).Returns(numberOfFields);
  12:   
  13:      // readCounter is used to allow the reader to be iterated
  14:      // incrementing readCounter in the callback allows the reader to be read a specific number of times
  15:      var readCounter = 0;
  16:      moqReader.Setup(x => x.Read()).Returns(() => readCounter < numberOfReads).Callback(() => readCounter++);
  17:   
  18:      return moqReader;
  19:  }

When I want to mock a result set that has only one row I use this:
   1:  private DbDataReader CreateDataReaderWithSingleResultSet(Dictionary<string, object> values,
   2:              int numberOfReads = 1, bool hasRows = true)
   3:  {
   4:      // get the basic fake database reader
   5:      var moqReader = CreateFakeDbDataReader(values == null ? 0 : values.Count, numberOfReads, hasRows: hasRows);
   6:   
   7:      if (values == null)
   8:      {
   9:          return moqReader.Object;
  10:      }
  11:   
  12:      // iterate the objects (fake data)
  13:      for (var i = 0; i < values.Count; i++)
  14:      {
  15:          var item = values.ElementAt(i);
  16:          // setup the reader to return the name of the "field" when the index is used on GetName
  17:          moqReader.Setup(p => p.GetName(i)).Returns(item.Key);
  18:          // setup the reader to return the value when it encounters the key
  19:          // this is where we specify that if reader["FirstName"] is evaluated, "Fake" (or whatever) will be returned
  20:          moqReader.SetupGet(p => p[item.Key]).Returns(item.Value);
  21:          moqReader.SetupGet(p => p[i]).Returns(item.Value);
  22:      }
  23:   
  24:      return moqReader.Object;
  25:  }

And when I want to mock a result set that has multiple rows I use this:

   1:  private DbDataReader CreateDataReaderWithSingleResultSetWithMultipleRows(List<Dictionary<string, object>> rows,
   2:              bool hasRows = true)
   3:  {
   4:      // get the basic fake database reader
   5:      var moqReader = CreateFakeDbDataReader(rows.First().Count, rows.Count, hasRows: hasRows);
   6:   
   7:      var currentRow = 0;
   8:   
   9:      // iterate through the "rows" of data
  10:      for (var i = 0; i < rows.Count; i++)
  11:      {
  12:          // for each "row" in the data, check if the current row is being retrieved
  13:          if (i != currentRow)
  14:          {
  15:              continue;
  16:          }
  17:   
  18:          var row = rows[i];
  19:          // iterate through the "fields" in the current row
  20:          for (var j = 0; j < row.Count; j++)
  21:          {
  22:              var item = row.ElementAt(j);
  23:              // setup the reader to return the name of the "field" when the index is used on GetName
  24:              moqReader.Setup(p => p.GetName(j)).Returns(item.Key);
  25:              // setup the reader to return the value when it encounters the key
  26:              // this is where we specify that if reader["FirstName"] is evaluated, "Fake" (or whatever) will be returned
  27:              moqReader.SetupGet(p => p[item.Key]).Returns(item.Value);
  28:          }
  29:      }
  30:   
  31:      return moqReader.Object;
  32:  }

The one part that I never got coded because I didn't really need to was returning multiple result sets.  I'm sure it can be done, but I haven't had to do it yet.  Happy coding!

Monday, January 18, 2016

Boot Camp

Let me start this post by saying that I'm not an Apple person.  I have a Windows phone (though I hate it; I prefer Android, but this one was cheaper) and Windows PCs at home and work.  I'm primarily a .NET developer (which you'll note is a Microsoft product).  I don't hate Macs (well, I didn't before this); I just don't use them.  They're more expensive than PCs and I don't know them as well as I know PCs.  I've had a PC since back when they were called IBM compatibles.

TL;DR: Boot Camp Assistant is probably great, but on old machines it sucks.  Snow Leopard with Boot Camp Assistant 3.2 is not as user friendly as newer versions probably are.  A Macbook Pro manufactured in 2009 cannot boot from a thumb drive.  When all else fails, just let the computer do it's thing.

All that said, I married an Apple person.  Everyone in her family is an Apple person.  I knew it when we decided to get married, but I didn't think it would be the one thing that finally made me crazy.  My mother-in-law got a new job, which requires her to have Windows on her home computer.  She has a Mac.  No problem, though, right?  Using Boot Camp Assistant, Mac will guide you through the painless process of dual booting OS X and Windows on the same machine.

I verified the specs, double-checked everything and got started.  It is now three days later and I'm almost finished.  Let me walk you through my troubles in the hopes that my trials and tribulations may help you in a similar situation.

I started off by launching Boot Camp Assistant and trying to download the drivers, as the instructions from Apple say to do.  No joy there.  A message came up that no drivers were found or something.  I honestly don't remember now; that was two days ago.  OK, no big deal.  A quick search of the Internet found the instructions that say I should just skip that part.  Got it.  Moving on then.  The next part of the wizard creates the partition for Windows.  At this point I'm happy.  I just want to make that clear.  At this point everything seems to be going according to plan.  The Chiefs are finally starting to put up a fight against the Patriots and it appears for the moment as though Apple's instructions will work fine.

But no.  Boot Camp Assistant can't create the partition because "some files could not be moved".  I find a video on YouTube where a guy explains how to get around it by running (what appear to be) some Unix commands.  That doesn't work either.  Some more searching of the Web and I find a solution to create a backup, wipe the drive, restore from backup and try again.  Well, that's not going to work for me.  I don't have an external hard drive to which to backup the OS and I don't particularly want to wipe the drive anyway.  I text the MIL and tell her no can do, boss.  She says I should go ahead and wipe it and reinstall.  Apparently she had anticipated this would happen and had backed everything up except her pictures.

OK.  Copy the pictures to a thumb drive, wipe the hard drive, copy the pictures back.  Got it.  How do I copy the pictures to the thumb drive?  It is now 11:30 PM on day 2.  I'm tired.  I'm falling asleep.  My wife is asleep next to me on the couch.  I want to quit.  OK.  Back to the Internet.  Ah, I need to "export" the files from iPhoto to the thumb drive.  Got it.  That was easy.  Time to wipe the hard drive.  Put the disc in and... hey, look at that... something went like it was supposed to.  Sweet.  I let it finish wiping and call it a night (although it is technically morning).

Surprisingly the reinstall goes pretty smoothly.  I will give Apple credit on this one: their install process is much faster than Windows (or so I thought at the time).  Great!  OK, OS X is installed.  Run Boot Camp Assistant again, partition the hard drive... and I'm done with the Mac side of things!  Woohoo!!

Getting to this point was supposed to be the hard part.  I had never done that stuff before.  From here on out it's just a matter of installing Windows, which I've done roughly a billion times.  Sweet.  I've got my bootable USB with a 32-bit copy of Windows 7 on it.  I've tested it to make sure it registers properly on my computer and we're good to go.  Put the thumb drive in when prompted for the disc and... Boot Camp Assistant doesn't see it.  Nor does it have any way to point to it.  Back to the Internet.  I see that I can install rEFIt and that should solve the problem.  Did that.  No joy.

After a lot more searching and a little bit of crying I decide I'll need to burn a DVD with Windows on it.  I don't have any blank DVDs.  To Walmart!  Back.  I make a bootable DVD.  I put it in the drive on the Mac.  I reboot the Mac and... nothing happens.  It just boots into OS X.  Oh, I forgot to hold the option key while it was booting.  No problem.  Reboot, hold option key (after the chime) and I get an option to boot to the Windows CD!  WOOOHOOO!!!  Choose that option and I'm home free... hahahahahahahahahahaha... no, that's not how it goes.

I end up on a black screen with a blinking cursor.  I wait.  I wait.  I wait.  I cry.  I wait some more.  I decide waiting isn't working.  Back to the Internet.  First suggestion: just wait.  Seriously.  Reboot into OS X.  Run Boot Camp Assistant.  Try to use the wizard.  It looks like it works, but I'm not going to get excited.  No.  I'm not going to.  Nope.  Nice try, Mac.  OK, what's this?  It looks like it's working!?  YES!!!!   Oh, wait, actually no.  It didn't work.

Reboot, hold option, choose Windows, black screen with blinking cursor, cry, yell, cry more, power down, walk away, drink, drink more, yell more, cry a little bit more, boot computer, forget to hold option... Windows is loading.  Holy crap.  No, this time I'm really not going to get excited.  I'm just going to stare at this screen through now-bloodshot eyes and wonder when this nightmare will end.  Wait, what's this?  It wants a product key?  I'm done?  HOLY CRAP IT WORKED!  I'M BRILLIANT!