Your unit tests are #rubbish #coding

This post might contain the answers to writing better unit tests… or it might not…

Having searched for examples, trying and hoping to find someone doing something vaguely similar to me, all I find are what I would call “bad examples”.

I’ve had and seen more arguments about unit tests than any other piece of code. There are always two sides to anything, but it does appear more like one person’s unit test is another’s nightmare. Over time I’ve improved the structure of my unit tests and learned what people are willing to accept for a unit test in a code review.

Here’s a bad test:

[Test]
public void TestCalculateOffsetFromUtcNow1()
{
  const int hours = 1;
  const int minutes = 2;
  const int seconds = 3;
  var now = new DateTimeOffset(2017, 9, 12, 22, 20, 0, TimeSpan.Zero);
  var instance = new ClassToTest(() => now);
  var result = instance.CalculateOffsetFromUtcNow(new TimeSpan(hours, minutes, seconds));
  var expected = now.UtcTicks
    + (TimeSpan.TicksPerHour * hours)
    + (TimeSpan.TicksPerMinute * minutes)
    + (TimeSpan.TicksPerSecond * seconds);
  Assert.That(result.UtcTicks, Is.EqualTo(expected));
}

Here’s a slightly better test:

[Test]
public void TestCalculateOffsetFromUtcNow2()
{
  // Arrange.

  const int hours = 1;
  const int minutes = 2;
  const int seconds = 3;
  var now = new DateTimeOffset(2017, 9, 12, 22, 20, 0, TimeSpan.Zero);
  var instance = new ClassToTest(() => now);

  // Act.

  var result = instance.CalculateOffsetFromUtcNow(new TimeSpan(hours, minutes, seconds));

  // Assert.

  var expected = now.UtcTicks
    + (TimeSpan.TicksPerHour * hours)
    + (TimeSpan.TicksPerMinute * minutes)
    + (TimeSpan.TicksPerSecond * seconds);
  Assert.That(result.UtcTicks, Is.EqualTo(expected));
}

Indeed, the internet examples I’ve seen often go no further than simple AAA comments, but we can do better still…

[Test]
[Description(@"
  GIVEN I have an instance of ClassToTest
  WHEN I call CalculateOffsetFromUtcNow with a TimeSpan
  THEN it returns the correct DateTimeOffset.
")]
public void TestCalculateOffsetFromUtcNow3()
{
  // GIVEN I have an instance of ClassToTest

  const int hours = 1;
  const int minutes = 2;
  const int seconds = 3;
  var now = new DateTimeOffset(2017, 9, 12, 22, 20, 0, TimeSpan.Zero);
  var instance = new ClassToTest(() => now);

  // WHEN I call CalculateOffsetFromUtcNow with a TimeSpan

  var result = instance.CalculateOffsetFromUtcNow(new TimeSpan(hours, minutes, seconds));

  // THEN it returns the correct DateTimeOffset.

  var expected = now.UtcTicks
    + (TimeSpan.TicksPerHour * hours)
    + (TimeSpan.TicksPerMinute * minutes)
    + (TimeSpan.TicksPerSecond * seconds);
  Assert.That(result.UtcTicks, Is.EqualTo(expected));
}

So, with a DescriptionAttribute and some simple commenting in the Gherkin style, we have made leaps and bounds in the maintenance quality of the test, without actually changing the code. Once you have comments to read, the name of the method is lost and becomes irrelevant; I find it only serves to find a failing test.

A common trap with TestCaseData is to want to use complex types, so you resort to TestCaseSourceAttribute, like so:

[Test]
[Description(@"
  GIVEN I have an instance of ClassToTest
  WHEN I call CalculateOffsetFromUtcNow with a TimeSpan
  THEN it returns the correct DateTimeOffset.
")]
[TestCaseSource(nameof(GetTestCases))]
public void TestCalculateOffsetFromUtcNow4(DateTimeOffset now, TimeSpan timeSpan, DateTimeOffset expected)
{
  // GIVEN I have an instance of ClassToTest
  var instance = new ClassToTest(() => now);

  // WHEN I call CalculateOffsetFromUtcNow with a TimeSpan
  var result = instance.CalculateOffsetFromUtcNow(timeSpan);

  // THEN it returns the correct DateTimeOffset.
  Assert.That(result, Is.EqualTo(expected), $"Expected a result of {expected}");
}

private static IEnumerable<TestCaseData> GetTestCases()
{
  // ( now , timeSpan , expected )
  yield return new TestCaseData( new DateTimeOffset(2017, 9, 12, 22, 20, 0, TimeSpan.Zero) , TimeSpan.FromSeconds(0) , new DateTimeOffset(2017, 9, 12, 22, 20, 0, TimeSpan.Zero) );
  yield return new TestCaseData( new DateTimeOffset(2017, 9, 12, 22, 20, 0, TimeSpan.Zero) , new TimeSpan(1, 2, 3)   , new DateTimeOffset(2017, 9, 12, 23, 22, 3, TimeSpan.Zero) );
  yield return new TestCaseData( new DateTimeOffset(2017, 9, 12, 23, 20, 0, TimeSpan.Zero) , new TimeSpan(1, 2, 3)   , new DateTimeOffset(2017, 9, 13, 0, 22, 3, TimeSpan.Zero)  );
}

Indeed, we now test for more values, our test code itself is reduced, but the values have moved into some other method that gets very messy as the number of parameters grows. I’ve spent many hours in the past going through TestCaseData constructor parameter lists and I can say that I don’t care much for it. Even just earlier today I banging was my head against the wall at some huge TestCaseSource methods, they are painful to read, painful to maintain and probably more burden than benefit.

Therefore, don’t move away from TestCaseAttribute in the first place, work around it, like so:

[Test]
[Description(@"
  GIVEN I have an instance of ClassToTest
  WHEN I call CalculateOffsetFromUtcNow with a TimeSpan
  THEN it returns the correct DateTimeOffset.
")]
//       ( now                         , timeSpan   , expected                    )]
[TestCase( "2017-09-12T23:20:00+00:00" , "00:00:00" , "2017-09-12T23:20:00+00:00" )]
[TestCase( "2017-09-12T22:20:00+00:00" , "01:02:03" , "2017-09-12T23:22:03+00:00" )]
[TestCase( "2017-09-12T23:20:00+00:00" , "01:02:03" , "2017-09-13T00:22:03+00:00" )]
public void TestCalculateOffsetFromUtcNow5(string now, string timeSpan, string expected)
{
  // GIVEN I have an instance of ClassToTest
  var instance = new ClassToTest(() => DateTimeOffset.Parse(now));

  // WHEN I call CalculateOffsetFromUtcNow with a TimeSpan
  var result = instance.CalculateOffsetFromUtcNow(TimeSpan.Parse(timeSpan));

  // THEN it returns the correct DateTimeOffset.
  Assert.That(result, Is.EqualTo(DateTimeOffset.Parse(expected)), $"Expected a result of {expected}");
}

Top tip #1: Spend the time to line up your values, it’s only a few spaces and makes it a lot more readable. Also repeat the parameter names and line those up, it helps a lot in larger tables of test data.
Top tip #2: I often make good use of enum parameters, they are easy to combine, you can drive logic from them that is pretty readable too.

My version of unit test good practice:

  1. Unit test code must fulfill the same quality criteria as the code it is testing.
  2. Comment the test method at the top with the intention, the requirements being tested, etc. Gherkin is a very helpful syntax for this.
  3. Don’t put Act Arrange Asset comments without anything else, it is just plain lazy and virtually useless. You can split up the test method comment and replicate where they apply to. Future you or a colleague will be glad you did.
  4. Avoid generated test case inputs (e.g. TestCaseSource), they make the tests less readable, though there are occasionally very good reasons for using them, just do so wisely.

My rules that aren’t universally accepted:

  1. Code is code, no snake_casing methods, for example if PascalCase is the language standard, then do that.
  2. The name of a test method does NOT matter, yes, I said that. I once renamed all my test methods with vague names, showed them to a colleague, they read the comment at the top and didn’t care what the name was. Though I do like my test methods to begin with the word Test, it’s usually because it is something like TestAbcDoesSomething and it stands out from helper methods within the test class.
  3. Don’t create a test class for each test you write, group them by the class that is under test.

My observations:

  1. Writing good unit tests is harder than writing good code.
  2. Unit testing when prototyping will slow you up.
  3. Unit testing improves the quality of my code. I believe I write good code, so I often question why I am writing a test… then I find an issue and I’m glad I did.
  4. The biggest enemy of a unit test is a refactor, minimizing copy and paste code in your tests will help reduce the burden. Also don’t fear deleting a test, just ensure those requirements are covered by new or other existing tests.
  5. A “good” unit test is something you are proud of and also something someone is willing to accept in a code review – fortunately, the two normally go hand in hand.
  6. SpecFlow should not be used for unit tests, this may be a whole post in itself, so I won’t dwell on it.
  7. A unit test will take on average twice or three times the amount of time the actual code took to write.

To see the examples all in once place: https://github.com/piddle/Piddle.NUnitExamples

I hope someone finds this post useful, please comment if you wish.

Advertisements

My new spare and ice weather bike #cycling

I bought a cheap-ish mountain bike (Coyote Oklahoma), swapped the tyres and fitted mudguards (which is more tricky than you might imagine). I’ve put over a hundred miles on it commuting this week, so it’s holding up fine:

I lock the front suspension and it rides ok, not as good as bluebell (my normal ride), but I can ride it when bluebell needs repairing or it’s icy and I don’t want to keep swapping tyres.

I like the idea of disc brakes, not only because they stop better in the wet, but more the fact that discs and pads are easier to replace than a rim.

It’s got 26″ wheels, which is a dying breed looking at the mountain bikes for sale at the moment.

The front mudguards don’t have a mount on the forks, so I improvised using cable ties, it seems pretty solid:

The disc brake and bolt gets in the way at the rear, but some spacers, longer bolts and bending them to fit:

A few more spaces nearest the chairing and it’s also pretty stable.

Next project is to do some work on bluebell… And ride the abomination until she’s done 🙂

Bluebell’s got racy #cycling

After several years of “going to”, I’ve finally fitted my old drop bars onto bluebell:

It pretty much fulfills what I wanted, but whether they stay long term is uncertain.

Here’s what I do know:

  • Cables needed replacing because they weren’t long enough, but fitting new cables wasn’t a bad thing, it’s been a while
  • Rohloff shifter on minotaur bar extended works ok
  • It’s about 1 minute faster, bringing the quickest commute down to 45 minutes, that’s a record!
  • It feels nicer when climbing
  • Flat bar brake levers are better
  • Flat bars with Ergon GP1s are more comfortable on the hands, arms and shoulders
  • Gear changing is more difficult compared with flat bars
  • I still can’t fit bar tape very well
  • Finally my “It’ll come in useful one day” actually paid off.
  • A shorter stem would put me in a better position, but I can live with it for a while (read forever)

Happy sweaty summer weather.

Gathering performance metrics using InfluxDB and Grafana #coding @grafana @InfluxDB

Collecting metrics in a log file is pretty effective when you have one process doing that logging. You have the data and can do whatever you need to with it. This approach has served me well for many years, but occasionally you want to log metrics from multiple processes to the same source at the same time. Trying to pull together data from 30+ log files and make sense of it is difficult. You can log it to SQL Server or some other datastore, but then what, more often than not you want to graph the metrics over time.

I thought I’d give some opensource tools a try, let’s walk through how to setup InfluxDB as the datastore and Grafana as the dashboard to make sense of it all.

Download

(please review licenses before doing so)
https://portal.influxdata.com/downloads#influxdb
https://grafana.com/grafana/download?platform=windows

I’m using https://dl.influxdata.com/influxdb/releases/influxdb-1.2.4_windows_amd64.zip
and https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.3.2.windows-x64.zip

Initial Setup

You will need to look further into security, licenses and your specific needs, but for me it’s as simple as extracting the two zip files.

Start the servers

Run influxd (.exe) from influxdb-1.2.4-1
Run grafana-server (.exe) from grafana-4.3.2\bin

Where does data get stored?

InfluxDB seems to use C:\Users\{current-user}\.influxdb
Grafana seems to use subdirectories.
Please let me know if you find more.

Getting a metric into InfluxDB

First you (like with any DBMS) need to create a DB instance, I did mine via the command line (though if you read the documentation you can do it via HTTP too):

influx -execute "CREATE DATABASE MetricDb1"
influx -execute "CREATE RETENTION POLICY "two_hours" ON "MetricDb1" DURATION 2h REPLICATION 1 DEFAULT"

Note: influx.exe is in influxdb-1.2.4-1, it is best to run from within that folder to avoid path problems.

As you can imagine, with those settings you are going to lose a metric some time in excess of two hours after it was written, so choose careful and read up on the documentation.

You might think the next command is CREATE TABLE… but it’s not required, it’s magic…

Inserting and selecting data from command line

For the insert the retention policy name is required, kind of like a schema name, but it’s very similar to SQL, though very reduced functionality:

influx -execute "INSERT INTO MetricDb1.two_hours cpu,host=sausages,region=eu_north value=0.69"

For the select you can leave off the retention policy when you can assume it is the default one, but do note the two full stops:

influx -execute "SELECT * FROM MetricDb1..cpu"

Or you can specify it:

influx -execute "SELECT * FROM MetricDb1.two_hours.cpu"

Inserting data from C#

using (var client = new HttpClient
{
    BaseAddress = new Uri("http://localhost:8086/"),
    Timeout = TimeSpan.FromMilliseconds(10000)
})
{
    await client.PostAsync("/write?db=MetricDb1", new StringContent("cpu,host=sausages,region=eu_north value=0.69"), CancellationToken.None);
}

Of course, opening the client every time is not how you would code it for a real application, but you get the idea.

Graphing it

  1. Open grafana web site http://localhost:3000
  2. Login with admin user, password admin (yeah, you might want to change that)
  3. From the Home Dashboad (should be the landing page when you login), click Add data source
  4. Give it a name
  5. Choose InfluxDB
  6. Url is http://localhost:8086
  7. Access is direct
  8. No Http Auth (until you configure some)
  9. Database is MetricDb1
  10. No User or Password (until you configure them)
  11. Click Add
  12. Go to @ (grafana logo) -> Dashboards -> New
  13. Click Graph
  14. Click on Panel Title then Edit. Or find some other way to edit the graph.
  15. On the Metrics tab you should be able to enter your query. I chose FROM two_hours cpu SELECT field (value) mean () GROUP BY time ($__interval) fill (null)
  16. Choose a Group by time interval, I chose >1s
  17. I wanted a bar chart, so I chose Bars option for Draw Models on Display tab.
  18. Save the dashboard when you are happy with it via disk icon or CTRL+S.

That should be all you need to get started and see data in the dashboard. There are options for auto-refresh and all manner of good things so do make sure you explore grafana. I find Last 5 minutes very useful to start with and Refresh every 5s.

Lighter tyres #cycling

I’ve always gone for the reasonably priced 26″ Schwalbe tyres. Marathon Green Guard were my normal all round choice for almost puncture free riding, but they are heavy, not Marathon Plus weight, but not light either. (see link below for comparison). The standard Marathon Kevlar (old) tyres were lighter but weren’t robust enough for my needs.

Years pass, and a more expensive tyre catches my eye, the Marathon Supreme and coming in around 440g it’s a good weight saving:

I’ve never had a folding tyre before, they look very wonky to begin with, but look good once fitted:

Because I had an old Marathon Plus on the rear, it’s reduced the weight by around 750g! I just hope they don’t yield punctures, the reviews are very positive, but only time will tell whether they are worth double the money!

The best comparison is http://www.bicyclerollingresistance.com/tour-reviews/compare/schwalbe-marathon-2015-vs-schwalbe-marathon-plus-2015-vs-schwalbe-marathon-supreme-2016

That site shows the weight saving and fractional rolling benefits too, but also less puncture resistance.

Tips learned this winter #cycling

I’ve been a bit lazy recently with writing anything down, but that doesn’t mean I’ve not been cycling or indeed discovering new tricks and tips.

Tip 1

This winter I loved my toe covers, cheap, easy to fit and I just leave them on my shoes with minimal hassle compared with overshoes. I own two pairs now. They keep my feet warm enough and even some water out too, enough to keep my toes dry on a wet commute. £9.50 at time of writing… http://www.wiggle.co.uk/dhb-toe-cover-overshoe/

Tip 2

Base layers are expensive, but don’t necessarily need to be bought from a cycle shop, you can save money by looking around. I recently bought four from a DIY shop at £8.49 each, unfortunately they are £9.99 at time of writing… http://www.screwfix.com/p/helly-hansen-kastrup-baselayer-crewneck-black-large-42-chest/55368

Tip 3

Disposable gloves for bike maintenance. I like nitrile ones at just under 8p per glove… http://www.screwfix.com/p/skytec-utah-nitrile-powder-free-disposable-gloves-blue-large-100-pack/1705g

Tip 4

Never give up, just because your gloves don’t keep your fingers warm, you just haven’t found the right ones yet… I like the look of lobster neoprene gloves… maybe I’ll try them next year.

Tip 5

For every rubbish tool you have there’s a good one that cost just a few pounds more, not every tool is worth the extra, but I’m definitely glad I spent the extra on decent wire cutters, chain whip, floor pump and tyre levers to name but a few. Last weekend I used my wire cutters again and I remembered how much I appreciate them every time I use them.

Tip 6

Muc Off or similar top brand of degreaser and bike cleaner. Degreaser is so good, I don’t use a lot over the course of a year, but every time I clean my chain and bike I’m glad I have it and can’t imagine how I ever cleaned it without it.

Tip 7

Ignore and pity the fools with mega expensive road bikes that don’t ride all year round, they may go fast in summer, but fast is expensive and a whole load more maintenance and hassle. Buy yourself a cheap and really awful road bike, try riding it to put yourself off the idea of wasting a lot of money on one.

Tip 8

Rags and old socks, don’t throw them in the bin, they are perfect for bike maintenance and cleaning, as are old scrubbing and tooth brushes.

Tip 9

On a slightly icy Monday morning, do be careful in the office car park, they won’t have gritted it…

Tip 10

Check brakes frequently in winter, a sticking caliper can be enough to help you loose traction on that office car park…

Happy cycling, spring is on its way!

New lights and rucksack #cycling

I’ve never been one to recommend without having used it for while. Enough evaluation time has passed and I’m now ready to extol the virtues of my recent purchases.

A bag is a bag? No, mostly I prefer an office pannier bag, a rucksack trumps in terms of convenience, but also gives you a sweaty back. Though the fancy bag helps a little with fancy looking back ventilation:

It’s still more sweaty than not having it on your back.

(Osprey Talon 22)

But I’m going to stick with it because it’s more convenient (and the blue matches bluebell nicely LOL). The overall weight is slightly less because I don’t need a rack either now. It is also nicer to not have the weight on the back wheel and the rattling around the contents used to get, so the bike moves over bumps better. I’m happy with the bag and my new setup.

Now, onto my favourite subject, commuting lights. See my last post for an introduction http://wp.me/p1Tdgj-11j.

Here they are:

Don’t ever be fooled by beam patterns, heat maps or photos, the only way of knowing how good something is is to get your hands on one and try it on a wet road. The packaging (and website) show impressive patterns, which never match reality.

The Busch + Müller IXON IQ Premium LED Front Light Set – 1922QMLA is pretty good and I like plugging it in rather than taking batteries out and charging separately. The fact that it takes AA batteries gives me a lot of options with this light. It also lasts around 5 hours on full power so I don’t need to charge it every day. It switches to low power when the batteries are low. Looking at run time for low power is pointless, I don’t buy a high output light to run on low power. If this were the only light that I’d bought, then I’d probably rave about it, but, the Lumotec IQ-X light is even better…

The 20 extra lux on paper doesn’t do it justice, it’s a great light, even on my old and low powered supernova S dynamo hub, it far exceeds the output of the IXON IQ Premium, however I will keep using both because I like to be prepared at the least favourable time of year. The IQ-X is a nice looking light too. Anyone wanting to get a dynamo light should definitely consider this light.

I consider both of these Busch + Müller lights affordable and reasonably priced, even when the Pound is weak against the Euro. So there’s no excuse for not treating yourself when your existing lighting isn’t up to scratch.