Showing posts with label Testing. Show all posts
Showing posts with label Testing. Show all posts

Friday, September 27, 2019

C# 8 Interfaces: Unit Testing Default Implemetation

When taking a closer look at C# 8 interfaces, we need think about unit testing. We (potentially) have code in our interfaces; how do we test it?
We can test default implementations with our own fake objects, however, we cannot test them with mocking-framework objects (at least for now*).
Once we take a closer look at some tests and understand what mocking frameworks do for us, this will make a bit more sense. Hopefully our favorite frameworks will give us an easy way to test default implementations in the future, but for now we have to do things a bit more manually.

The code for this article is available on GitHub: jeremybytes/interfaces-in-csharp-8.

Note: this article uses C# 8 features, which are currently only implemented in .NET Core 3.0. For these samples, I used Visual Studio 16.3.1 and .NET Core 3.0.100.

This article uses 2 projects. UnitTests.Library is a .NET Standard 2.1 class library that contains the interface. UnitTests.Tests is a .NET Core 3.0 NUnit unit test project; this contains the tests.

*Update April 2022: The Moq mocking framework does now support testing default implementation by using the "CallBase" property (see notes in the Moq section below). In addition the Rocks framework (available on NuGet + GitHub repo) does support testing default implemented members. This framework was not included in my original survey, but I know the author, and he added the functionality after I showed him the issue.

Testing Default Implementation
I am a big believer in unit testing. When I put code into my projects, I think of how the code can be written so that it is easy to test. The same is true when it comes to default implementation in interfaces. The basics involve just a few steps.

An Interface
Here's the interface that we use for these tests, IRegularPolygon (from the IRegularPolygon file in the UnitTests.Library project):


This interface describes a regular polygon -- meaning, a shape that has 3 or more sides where each side is the same length. The "GetPerimeter" method has a default implementation, and this is what we want to test.

A Fake Object
For the first test, we will use a fake object. This is a manually-created class that implements the interface.

Here is the code for the FakeWithDefault class (from the IRegularPolygon.Tests.cs file in the UnitTests.Tests project):


This class provides implementations for the abstract members of the interface, but it does *not* provide an override for the GetPerimeter method. It relies on the default implementation from the interface.

A First Test
Now that we have the interface and an implementing class, we can write our first unit test. Here is the code for that (from the IRegularPolygon.Tests.cs file -- the rest of the code samples will come from this file):


This test creates an instance of our fake object and assigns it to an IRegularPolygon variable.

IMPORTANT:
It is important to specify that the variable is "IRegularPolygon". If we use "var" or "FakePolygonWithDefault" as the type, then the GetPerimeter method will not be accessible.

The next line calls the GetPerimeter method on the interface. This will use the default implementation.

The last line checks the value of the output. Our fake object uses the values of "4" and "5" for NumberOfSides and SideLength, respectively. So we expect that the value of GetPerimeter is 4 * 5, or 20.

This test passes:


So we can see that we can test a default implementation with just a few steps. We create a class that implements the interface but does not override the default implementations from the interface. In the test, we create an instance of that class and cast it to the interface. Finally, we call the member with the default implementation and check the result.

Testing with a Mocking Framework
Sometimes I manually create fake objects for my unit tests, but I generally use a mocking framework instead. This reduces the number of objects in the unit test projects. And once we're comfortable with a particular framework, we can quickly create test objects with a variety of configurations.

Unfortunately, we cannot test default implementation with the way that mocking frameworks work today. Let's start by looking at an example using Moq (the framework I generally turn to).

Creating a Mock with Moq
The test project includes a NuGet reference to the Moq package. With that in place, we can write a test that uses a mock object:


When using a mocking framework, we tell the framework how to create and configure our test object. The first line of our test creates a mock based on the IRegularPolygon interface.

The next two lines set up the mock with specific values for the NumberOfSides property and the SideLength property. In this case, if we ask the mock for "NumberOfSides", it will give us "3"; if we ask for "SideLength", it will give us "5".

The setup does *not* include configuration for the "GetPerimeter" or the "GetArea" methods. This is a key feature of mocking frameworks, and we'll discuss this further down.

The 4th line calls the "GetPerimeter" method on our mock object. (The variable "mock" is a "Mock<IRegularPolygon>". When using Moq, the "Object" property represents the "IRegularPolygon" object.)

The last line checks the results.

Failure
Unfortunately, we do not get the results we want.


This test fails. When we look at the Error Message, we see that the value was "0.0d" (meaning 0 as a double) instead of the expected "15.0d".

Expected Behavior
This is the expected behavior from Moq (and mocking frameworks in general). When we configure a mock object, we only need to fill in the items that we use. And that can be a big time saver, particularly if we need to quickly mock up a dependency needed for a test.

When we create an object manually (FakePolygonWithDefault), we need to provide implementations for all of the abstract members of the interface. This means that we have to provide an implementation for "GetArea" even though we do not use that method in the tests.

When we create a mock object using a framework, we only need to provide implementations for the things we care about. The mocking framework will take care of the rest.

In the unit test using Moq, we do not need to provide a configuration for the "GetArea" method because we do not use it in the tests. If, for some reason, a test calls the "GetArea" method, the mocking framework will return the default value. Since "GetArea" returns a double, the default value is "0.0d".

Does this value look familiar? We *are* using the "GetPerimeter" method in our tests. But instead of getting the default implementation from the interface, we are getting the default value from the mocking framework. Since "GetPerimeter" also returns a double, the default value is "0.0d". This is the value we see in the failing test.

***Update for Moq (April 2022)***
Moq has been updated to support calling default implementation for members. In order for the default implementation to be used, set the "CallBase" property on the mock object to "true".

Sample:
    var mock = new Mock<IRegularPolygon>();

    mock.CallBase = true;
    mock.SetupGet(m => m.NumberOfSides).Returns(3);
    mock.SetupGet(m => m.SideLength).Returns(5);

    double result = mock.Object.GetPerimeter();

    Assert.AreEqual(15.0, result);
This has the desired effect. The default implementation for "GetPerimeter" is called, and the test passes.
***End Update (April 2022)***

Explicit Configuration
We can explicitly configure the mock object with the "GetPerimeter" method:


And it works. This test passes:


But this does not use the default implementation; it uses an explicit implementation.

Other Frameworks
I tried a couple of other frameworks: NSubstitute and FakeItEasy. These both provided similar results.

NSubstitute
Here is a test using NSubstitute without an override of the default:


And with an override:


And the test results are similar to using Moq:


Again, this isn't a surprise if we understand how the mocking frameworks work.

FakeItEasy
Here is a test using FakeItEasy. First without an override of the default:


And with an override:


And the results:


Again, we see similar behavior to the other frameworks.

Will Mocking Frameworks Change?
All of this is still really new. C# 8 with default implementation released at the beginning of this week. So the fact that these mocking frameworks do not support default implementation by default is not surprising.

Will this change in the future?

I'm not sure. I have not done extensive searches. But in my preliminary search I haven't seen anything about default implementation, and these particular packages do not currently have pre-release versions on NuGet.

Update: As Blair Conrad notes in the comments, FakeItEasy has an open issue for supporting default implementation: Issue - Support for calling default interface members.

Here are the versions that I used for this project:


I will keep an eye on this because I'm curious how mocking frameworks will adjust with default implementation in interfaces.

It may be difficult to change the default behavior. If someone does *not* configure an interface member, do they expect to get the default value (current behavior) or the default implementation from the interface (new behavior)? Changing the current behavior could result in breaking changes.

***UPDATE (April 2022)***
As noted above, the current version of Moq (4.17.2) does support testing default implementation. Unfortunately, I do not know the exact version when the behavior was updated. The current version of FakeItEasy (7.3.1) does not support default implementation. The issue mentioned above is still open. I haven't dug into NSubstitute yet (it's on my list). The default behavior is still the same, but I need to see if there is a property or setting that makes it possible to test default implemented members.

Testing Manually
Whichever way the mocking frameworks go, we can test default implementations manually. We can create classes that do not override the default and then use those objects in our tests.

Default implementation in interfaces gives us a lot of new things to think about. I'm most concerned with how it affects the existing code, techniques, and processes that I already have (and other people have). Once we get the current things worked out, it will be easier to figure out how to move forward in the best way we can.

Happy Coding!

Thursday, January 10, 2019

More DI: Unit Testing Async Methods

I'm a big believer in unit tests. I've seen how unit tests make me a faster developer (check out my unit testing materials for more information). There are a few quirks when we're testing asynchronous methods.

Last time, we looked at a decorator class that adds retry functionality (More DI: Adding Retry with the Decorator Pattern). This time, we'll write some unit tests to make sure the class behaves as expected.
There are a few quirks when we're testing asynchronous methods.
This article is one of a series about dependency injection. The articles can be found here: More DI and the code is available on GitHub: https://github.com/jeremybytes/di-decorators.

The Unit Under Test
We're testing the retry functionality in our decorator. This is in the "PersonReader.Decorators" project, RetryReader.cs file, specifically the "GetPeople" method.


This method should try to call a method (up to) 3 times. If the call is successful, it returns the results. If the call throws an exception, then it waits 3 seconds and tries again. For details on this method, see the previous article.

This class wraps another data reader (such as a ServiceReader) that provides the actual data. In our tests, we will create a fake object to handle this functionality.

For testing, we want to test 3 scenarios. These are the same 3 scenarios that we checked by manually running the application in the previous article. (1) Success on the first try; (2) Failure on the first try with success on a subsequent try; (3) Failure after exhausting the retry attempts.

Let's head over to the tests.

Unit Testing the Retry Reader
We already have a test project set up: PeopleReader.Decorators.Tests. The completed tests are in the RetryReaderTests.cs file. Let's walk through creating the first test.

The first test will ensure that the Retry Reader returns data when there are no problems with the wrapped data reader -- the "happy path". Here's a start to that test:


First, about the name: I use a 3-part naming system. The first part is the unit under test ("GetPeople"). The second part is the state that we're testing (that the wrapped reader is "NotBroken"). The third part is the expected outcome (that the method "ReturnsPeople" successfully).

We start by creating an instance of the RetryReader class that we're testing. The constructor needs a parameter which is an IPersonReader (the "real" data reader that we're wrapping). Since we don't want to use a real data reader, we'll create a fake one.

A Fake Reader
The fake reader will need to implement the "IPersonReader" interface (from the "Common" project, IPersonReader.cs file).


The interface has 2 methods that both return Task, so we're dealing with asynchronous methods here.

For the fake reader, we want to be able to control whether it returns successfully or throws an exception. So we'll create a class that has a "brokenCount" value. This value determines how many times the method should fail before it succeeds.

Here's the code for the BrokenReader (in the "PersonReader.Decorator.Tests" project, BrokenReader.cs file):


The first thing to note about this class is that it implements the "IPersonReader" interface, so it has the 2 members "GetPeople" and "GetPerson".

At the top of the class, we have some fields to help us. The "brokenCount" holds the number of times the class should fail before it returns successfully. If the "brokenCount" is 0, then all calls will be successful. If the "brokenCount" is 1, then the first call will fail, but the second call will succeed. This value is set by a constructor parameter.

The "retryCount" is used internally to keep track of how many calls have been made so far.

The "testPeople" field is our test data that is returned when the call is successful.

The "GetPeople" method returns a Task<IEnumerable<Person>>. Before we look at the final code, let's walk through a few things we might try.

First, we might try to write the method as if we didn't have to worry about Task:


The "if" block checks to see if we've hit the threshold. If the retry count is less than the broken count, the we want the call to fail by throwing an exception. The retry count is also incremented so the value will be different with the next call.

For the "return" of this method, we try to return "testPeople". But this fails because "testPeople" is a List<Person>. List<Person> implements IEnumerable<Person>, so those types are compatible, but we really need a Task<IEnumerable<Person>>.

There are a couple of approaches. One option is to wrap the "testPeople" in a Task:


This option works, but I'm not a big fan of the syntax. It's a bit difficult to read, particularly for people who aren't well-versed in using Task directly.

The next option feels like a bit of a cheat, but I like it quite a bit better:


In this code, we "await Task.Delay(1)". This will pause operation for 1 millisecond. But the more important thing is that when we "await" something in a method, the whole method becomes asynchronous (we also need to mark the method with the "async" modifier).

When we changed the interface to async methods (More DI: Async Interfaces), we saw that when we "await" something in a method, the return value is automatically wrapped in a Task for us.

In this method, that means we can "return testPeople", and it will automatically be wrapped in a Task.

Using "await Task.Delay(1)" is not something I would use in production code -- it is an artificial delay after all. But for unit tests, I'm willing to take the slight performance hit so that the tests are more easily approachable.

Update 1/10/2019: 
As Graham King points out in the comments, another option is to use Task.FromResult() to create a completed task with the desired result. This is a good option (that I completely forgot about), so let's take a look.

Here's the "GetPeople" method using Task.FromResult:


The idea is that "Task.FromResult" will create a completed Task that has "testPeople" as the result. Unfortunately, the compiler is not happy here. The error tells us that we have a type mismatch between the expected result (IEnumerable<Person>) and the actual result (List<Person>, which is the "testPeople" type). Even though List<T> implements IEnumerable<T> and "testPeople" is an IEnumerable<Person>, Task needs us to be more specific.

To fix the code, we can add a generic type to the "FromResult" call:


Or we can add "AsEnumerable" to the "testPeople" parameter:


But probably the best option is to change the type of the "testPeople" field from "List<Person>" to "IEnumerable<Person>":


Then we don't have to worry about the type coercion.


This feels a lot better, and this is what you'll see in the final code for this class (BrokenReader.cs file). Thanks, Graham!

Using the Fake Broken Reader
Now, we'll flip back to our tests and create an instance of the "BrokenReader" and pass it into the RetryReader constructor (in the RetryReaderTests.cs file).


After creating the RetryReader instance, we need to call the "GetPeople" method. It's really tempting to use the code here because this is the way I normally code up the "action" part of the unit test.

The danger is that it's easy to assume that "result" is the Person data. But if we inspect the variable type, we see that it is actually a Task:


A clue to how we should actually write the code is in the "Usage" section of the popup. Instead of simply calling the "GetPeople" method, we should "await" it. This will give us the "IEnumerable<Person>" that we can use for the assertion.

Here's the updated test (which is the final code in the RetryReaderTests.cs file):


Update 1/15/2019: A "retryDelay" parameter has been added to the RetryReader constructor. This lets us control the delay between retries. The updated code will be shown below.

As noted above, we now "await" the GetPeople method. Because we are using "await", we must also mark the test method with the "async" modifier.

Another thing that we need to change is the return type of the test. Previously, it was "void", but when we return "void", we lose all visibility to the Task, including any exceptions that might be thrown. So we need to change the return type to "Task". The good news is that if we forget to do this, the testing framework will remind us when we try to run the test.

In the assertion, we are checking to see that the "result" coming back from the GetPeople method is not null. We could also do a more specific check for the 2 items in the data, but we're actually more concerned that this does not throw an exception.

The last thing I did was change the name of the test to have "Broken0" in the name. I'm not sure I like this name, but it goes along with the next 2 tests. I'm thinking about changing the names of all of the tests, so they may be a little different in the final code.

Another Test
The next test will see if the retry functionality works. We want the fake reader to fail on the first call, but succeed on the second one. Here's what that tests looks like (from the RetryReaderTests.cs file):


This test is almost identical to the previous test. The difference is that the "BrokenReader" class is passed value of 1, meaning we want the first call to fail, but the second call to succeed.

The rest of the test is the same. We expect that we will get data back (eventually) and that we will not get an exception.

The caveat is that this test is slooow (and that's why I gave it a category). This test takes at least 3 seconds to complete since the RetryReader waits 3 seconds before retrying the method call.

It's not great, but it's an accurate test of the functionality. The category attribute gives us a chance to filter these tests in the test runner so that we're not constantly running them with the rest of the test suite.

Update 1/15/2019: A "retryDelay" parameter has been added to the RetryReader. This lets us set the value to "0" for unit testing, so we get immediate retries, and the tests no longer run slowly. Here is the updated code for this test (similar updates were made to the other tests and are available on GitHub.)


This test sets the retry delay to "0", so this is no longer a slow test.

Testing for Failure
The last test is to see what happens when all of the retries fail. In this case, we expect that the original exception from the wrapped data reader will be thrown.

This test looks a bit different from the other 2 (this is in the same file):


This time, we give the "BrokenReader" a value of 3. So it should fail on 3 calls before returning successfully. Since our RetryReader only tries 3 times, we expect that the overall call will fail.

The "try/catch" block is set up to ensure that we get the expected exception when the "GetPeople" method is called. For more information on this pattern (and a couple others), take a look at Testing for Exceptions with NUnit. I opted to go with the option that is not test framework specific.

In the "try" block, if there is no exception thrown, then we hit the "Assert.Fail" call. This will fail the test.

If an exception is thrown, then we hit the "catch" block. The "Assert.Pass" is not strictly necessary; if we leave the catch block empty, this test will still pass. But I like to add the "Pass" to make it more clear that an exception is what we expect to happen here.

This is also a sloooooow test (it takes at least 6 seconds). So we've marked it with the "Slow" category as well.

Update 1/15/2019: A "retryDelay" parameter has been added to the RetryReader. This value can be set to "0" in the unit tests so that they are no longer slow.



Test Results
All of these tests pass:


This also shows the timings, and we can see that the slow tests do take 3 seconds and 6 seconds, respectively.

Update 1/15/2019: With the "retryDelay" values specified above, the tests run much more quickly:



Async Unit Tests
Overall, having async methods in unit tests is not that much different from having async methods in our production code. We can use async/await with our test frameworks (just remember to return "Task" instead of "void" on the test methods).

When we fake up data, we often don't need to make async calls to a data store. As a cheat that keeps the code readable, we can "await Task.Delay(1)" in our fake objects. This will make the method asynchronous and automatically wrap the return value in a Task.

There's a lot more to explore with this project, including the other decorators, configuration, and how .NET Standard and .NET Framework projects interact. So stay tuned for more.

Happy Coding!

Monday, August 28, 2017

Jeremy on Cross Cutting Concerns

While I was at Detroit.Code(), I had the chance to sit down with Matthew Groves (@mgroves & website) and talk a bit about unit testing. You can hear the conversation on Matthew's Cross Cutting Concerns podcast.

Cross Cutting Concerns:
Jeremy Clark Convincing Your Boss on Unit Testing


We recorded this right after I did my talk "Unit Testing Makes Me Faster: Convincing Your Boss, Your Co-Workers, and Yourself". It's a great talk that I love to do, but I wish that I didn't need to convince people that unit testing is a good idea.


The problem is that many developers have had bad experiences with ineffective and/or useless tests. If you don't get the benefit, then it really is a waste of time. But when we take a closer look at what constitutes good tests, we find that they can make us more effective, efficient, and even faster.

Be sure to check out my upcoming events where you can see this talk in person. The next time will be a Visual Studio Live Chicago in mid-September. Feel free to contact me if you'd like me to come out to your company, developer event, or conference and help your group see the advantages of testing.

And if you'd like a deeper dive, I offer a full-day workshop to get developers started with good unit testing practices.

Happy Coding!

Friday, June 30, 2017

Book Review: Working Effectively with Unit Tests

I recently finished reading Working Effectively with Unit Tests by Jay Fields (Amazon link). I'm a bit mixed on whether I would recommend this book. There are some good unit testing tips, but it wasn't especially memorable, and there were a few things that I didn't care for too much.

The short version is that my main recommendation for unit testing techniques will remain The Art of Unit Testing by Roy Osherove (Jeremy's review).

Things I Liked
Keep What Works
There were a few general messages that I really liked. The first is try stuff and keep what works. I always prefer a non-dogmatic approach because not every technique works in every environment. I really appreciate that Fields talks about some of the things that he's done in the past, liked them, but then later stopped using them because they no longer fit his situation.

Descriptive Tests
Another message that I really liked is to keep tests DAMP (Descriptive And Maintainable Procedures) as opposed to DRY (Don't Repeat Yourself). Keeping common code centralized is a good practice for our production code, but testing code is a bit different. Tests are really meant to be independent, isolated, and not interact with each other, whereas our production code needs to be cohesive and collaborative.

This really follows my general advice to make sure that tests are readable. It's good to be a bit more verbose and explicit so that they are very easy to approach when we need to look at the actual test code.

Setup Methods
As far as specific recommendations, there are several that I found useful. First is generally avoiding setup methods. By keeping setup (the "Arrange" step) local to the test, it enhances readability and helps make sure we are only using the things that we need for a particular test.

For example, in a test class-level setup method, we may have multiple objects instantiated with various states that we can use in different tests. This means that we could have objects that are instantiated in a setup but are not actually used for a test. This means that we've got some wasted code, and it also makes it a bit harder for us to determine exactly what's important for a particular test.

I've explored something along these lines (Unit Testing: Setup Methods or Not?), although I tended to use factory methods that are explicitly called to get some of the non-critical bits out of the tests themselves. Fields' technique is definitely worth exploring some more.

Assertions
There are a couple pieces of good advice regarding assertions, including one assertion per test. This is particularly important because most testing frameworks use exceptions for tests. So when the first assertion fails, no code after it will run. If we have several assertions, we don't know if we have a single failure or multiple failures. If we keep one assertion per test, then we can tell where our problems are. This also goes along with the DAMP approach; we don't need to be afraid of duplicating behavior in unit tests.

Another piece of advice is to assert last. The thing that we are actually verifying should be at the very end of the test method. This makes it really easy to find what we're testing. And this also goes along with the "Arrange/Act/Assert" layout which I really like.

Fields also spends time talking about testing for exceptions and some of the weirdness that is caused by using a try/catch block. When using a try/catch block, the assertion is in the middle of code (usually in the catch block), and we also need to have a "fail" in the middle of the code if an exception is not thrown.

To get around this, Fields suggests making a custom assertion method, Assert.Throws(), that can be used to check for exceptions without a try/catch block, and can also be put at the end of the test. That way we can follow the "assert last" advice. This is similar to the "Assert.Throws()" that is provided with NUnit (and other frameworks that provide custom assertions). I wrote a bit about this in Testing for Exceptions with NUnit.

Things I Have Mixed Feelings About
Solitary and Sociable
There were a few things that I had mixed feelings about. One of them had to do with Solitary Unit Tests vs. Sociable Unit Tests.

A solitary unit test is a test where only 1 object is "new"ed up. Everything else is some sort of test double, like a stub or mock. A sociable unit test has multiple objected "new"ed and checks the interaction between them.

I don't really like the definition of sociable unit test because to me that steps outside of the world of unit testing and starts moving into integration testing. Fields does mention integration testing, but he looks at that as more of an end-to-end type thing. I've generally looked at integration testing as checking that the objects work together at different levels -- from localized to end-to-end.

This isn't really important in the grand scheme of things. I just seem to put all of my "unit tests" into the "solitary unit test" bucket.

Fields does make the recommendation of separating the solitary unit tests and sociable unit tests. The solitary ones are generally very fast and we want to run those very often, and the sociable ones may take a bit longer (for example, if there are I/O operations) and we probably run those less frequently. This is very good advice.

Sample Scenario
Another thing that I was mixed about is the sample scenario. I do like that the same application code was used throughout the entire book. But the scenario was a video rental store. Since the book is from 2014, this example was a bit out of date when it was published. As someone who is well over 40, I have no problems remembering what it was like to rent movies from a physical store. But I'm sure there are lots of developers today who have not had that experience.

Again, not a big deal. The samples could easily be updated to use a video kiosk rather than store.

Things I Didn't Like
The First Test
While I like that the same scenario is used throughout the book and that there was a focus on continuously improving the tests, I really did not like the first example.

The reason is that Fields states, "Let's get straight to code" and then shows a rather-difficult-to-read example and says "you don't need to understand this." From my perspective, that defeats the purpose of going straight to code.

Motivators
Another thing that left a bit of a bad taste had to do with test motivators. Fields spends some time telling us that it is important to understand our motivation for writing tests in order to write effective tests that match the motivation. He also spends quite a bit of time listing different motivators. The problem is that these motivators are not used in the rest of the book (unless I missed it). So it looks like we're being told that it's important to understand the motivation, but doesn't tell us how that impacts things in a practical way.

Naming
Another thing I don't agree with is Fields' opinion on naming tests. He likens test names to comments. Since the test methods are never called directly, the test names are unnecessary and at worst can add confusion. I agree that test names are comments, but I have seen usefulness in that. When a test has a good name, when it fails I can tell what happened simply by looking at the test explorer; I don't need to dig into details to see what went wrong (at least, I don't need to dig into the test details nearly as often).

I would liken test names more to variable names than comments. When we have useful variable names, it enhances the readability of our code. This is why I'll often include an intermediate variable. Then I can include some "comments" about what it is doing by giving it a good name, even if the code itself is not that hard to understand.

The Last Test
While I like most of the techniques presented, I wasn't a big fan of the final test state. His use of Test Data Builders throughout the book were interesting, and they gave me some ideas that I would like to work through. But the conclusion was a bit of a logical extreme:


Here's the text

public class CustomerTest {
  @Test
  public void chargeForTwoRentals() {
    assertMoney(
      5.7,
      a.customer.build().addRentals(
        create(
          stub(Rental class)
            returning(a.money.w(2.2).build())
            .from().getCharge()),
        create(
          stub(Rental class)
            returning(a.money.w(3.5).build())
            .from().getCharge()))
      .getTotalCharge());
  }
}

For someone walking up to this test for the first time, it is a bit confusing. I think it's because the Arrange, Act, and Assert all get mixed together. Fields promotes this as a good choice because it does follow "assert last" (although it's basically crammed the entire test into the assertion).

To pick this apart a bit, the behavior that we're testing (the "Act") is at the very end: "getTotalCharge()". The "Arrange" is really everything after "a.customer.build()..." which creates an object with test data so that we can call "getTotalCharge()" on a populated object. The "Assert" is the last step, but also the first step (which is weird in my mind).

I think that the test data builders can be very useful, but I would really like to see a standard "Arrange/Act/Assert" layout with some intermediate variables for readability.

Wrap Up
While Working Effectively with Unit Tests by Jay Fields does have some good advice, I think the drawbacks keep me from making it a recommendation. For general testing advice, I'd still go with The Art of Unit Testing by Roy Osherove.

Happy Coding!

Monday, April 24, 2017

TDDing into a Fibonacci Sequence in C#

A few days ago, I mentioned how I wanted to do a bit of experimentation with a Fibonacci Sequence implementation. Before I did my experimentation, I wanted a working sequence and a set of tests to validate that. This way I would know if my refactoring broke something. [Update: here's some of that experimentation.]

So let's TDD into a Fibonacci Sequence. You can grab the code from GitHub: jeremybytes/fibonacci-tdd. There are branches to go along with each step.

Initial Project
For the initial project, I created a console application and a class library to hold my tests. I figured that I could put my Fibonacci sequence class in the console application and then move it to its own project if needed.

We'll basically be looking at 2 files. The first is "FibonacciSequence.cs", and (as mentioned) this is in the console application:


Then we have the test, "FibonacciSequenceTests.cs":


Since we're going to be writing tests to make sure we've got the sequence right, I included the first 12 Fibonacci numbers. If you're not familiar with the Fibonacci sequence, each value is determined by adding the previous 2 values. So the 6th value (8) is determined by adding the previous values of 3 and 5.

The First Test
So let's write a test for the first element in the sequence. But before we do that, I'm going to do a bit of setup.

Since I want this to be a sequence (which means IEnumerable in the C# world), I'm going to stub out the interface in our production class:


I know that this is writing code before tests. But since we know we want a sequence, it makes sense to give ourselves a framework to hang our code on. Notice that even though I've added the code for the "IEnumerable<int>" interface, we haven't included any implementation. We'll write our test first.

And here's the first test:


This creates an instance of the sequence, pulls the first value, and then checks to see that it's "1".

This tests fails (as expected):


The reason for the failure is the "NotImplementedException" that gets thrown by our class.

So let's write the very simplest code possible to get this to pass:


The "yield return" will create an enumerator for us in the background so we don't have to deal with it explicitly. For more information on IEnumerable, you can check out this article series: Next, Please! A Closer Look at IEnumerable.

This code is a bit too simple. We know that it doesn't really fulfill our sequence needs. But it does get our first test to pass:


So let's move on to the next test.

Testing the 2nd Element
Now that we've got things set up, it should be easier to write additional tests. Lets set up a test for the 2nd element in the Fibonacci Sequence:


Testing the first element of a sequence is easy. Testing the second element is a bit trickier. What I did here was use the "Take" method (one of the awesome LINQ methods) to grab the first 2 items of the sequence. Then I take the "Last" value.

But there's a problem with this test. It passes:


Yikes. That's not good. When doing TDD, I expect the test to fail until I add proper implementation code. So what's the problem?

The "Take" method will grab up to the number of values requested. In our case, the sequence only returns a single item. But "Take" doesn't care; it grabs as much as it can. Then when we ask for the "Last" value, we end up getting the only one that's there, which is the first value.

Correcting the Test
When we have a test that passes when we expect it to fail, we've either got a problem with our code or a problem with our test. In this case, the problem is with our test. "Take" is not the appropriate LINQ method to use here. Fortunately, there's another LINQ method we can use: "ElementAt":


One thing to keep in mind is that "ElementAt" uses a 0-based index. So using "1" will get us the 2nd item in the sequence.

Now our test fails:


This is better. The reason for the failure is that there is no 2nd item so "ElementAt" throws an exception.

For this implementation, we'll do something very simple:


I'm feeling like this is too simple still. But I'm also thinking that we can get a little more info to let the method take shape a little more naturally before we think about refactoring.

This is enough to get the test to pass:


So let's keep moving.

Testing the 3rd Element
Next we'll write a test for the 3rd element, which we expect will be "2":


This test fails (as expected), so let's write a bit more code:


This gets our test to pass, but it's kind of stupid to keep writing the method this way. So, I'm going to to a bit of refactoring.

Refactoring to Something a Bit More Useful
Since I see a little bit of a pattern forming here, I'm going to add a "for" loop to the implementation:


I know that this won't work for the entire sequence. But it works for the tests that we have in place right now. We'll worry about fixing this in a bit, but not until our test cases call for it.

Parameterizing the Tests
It looks like we're writing the same test over and over again. This is where I look to see if we can parameterize the test. In this case, I think it will work pretty well.

Here's a new test:


I'm using NUnit, so I can set up test cases that get passed in as parameters. For more information, take a look at this article: Parameterized Tests with NUnit.

This will plug the values of the TestCases into the parameters of the test. So in the first case, it will use "0" for the "ElementAt" call (which is the first item in the sequence) and it will use "1" as the "expected" value in the assertion.

This lets us test the first 3 elements of the sequence with a single test. And our results show the tests are passing:


Notice that the results show the parameter values plugged in. This is really useful when one (or more) of these test cases fail.

Testing the 4th Element
Testing the 4th element of the sequence is as simple as adding another test case:


This test passes without any changes to our code. And that's okay. We expect the 4th element is "3", and that's what our code returns.

It's interesting to see that the simple "for" loop that we built in the code works for 3 elements in the sequence. But we'll see it break down with the next element.

Testing the 5th Element
Adding the test case for the 5th element creates a failing test. I won't show a screen shot, but the test just has "TestCase(4,5)" added.

Now we have to do a bit of math to really implement the Fibonacci Sequence:


Like the description of the Fibonacci Sequence, I add together the previous two values. A couple of local variable hang on to those values so they can be used to calculate the next item.

With this in place, all of our tests pass.

Refactoring
The tests are passing, but I see some dead code in the implementation. We are no longer using the indexer of the "for" loop. Since we don't need the indexer, we can swap the "for" loop for a "while" loop:


I'm never a huge fan of "while(true)", but it is a good way to create an infinite sequence.

The First 12 Elements
Since we've implemented the definition of the Fibonacci Sequence, we would expect that additional tests would pass. I set up the test cases for the first 12 elements:


And all of the tests pass:


Yay!

But there's a problem.

Overflow!
I've done a lot of experimentation with the Fibonacci Sequence. (I'm not sure why it intrigues me so much.) One thing I know about it is that it will overflow a 32-bit integer pretty quickly. How quickly?

Let's set up the console application to find out. We'll add code to our console application to print out the first 50 values of the Fibonacci Sequence.


And here's the output:


As we can see, something weird starts to happen at item #47. We overflow the standard integer and end up with a negative number (since the default 32-bit integer is signed).

Testing for Overflow
Now that we know where our problem is, we can create a test for it.


This test grabs elements #46 and #47 (remember, "ElementAt" is 0-based). Then we check to make sure that #47 is greater than #46.

Since this is the pivot-point of the 32-bit integer, element #47 give us a negative value, so it is *not* greater. This means our test fails.

Fixing Overflow
To fix the overflow, we'll change from using "int" (a 32-bit integer) to using a "long" (a 64-bit integer). The code is fairly easy to update at this point.


There's also one change we need to make in our tests. The "expected" parameter type needs to be changed to "long".


With this in place, we now have all of our tests passing (including the one that checks for overflow):


And we can see our console application is behaving as expected as well:


Wrap Up
This code isn't perfect. Our implementation returns an infinite sequence (because of the "while(true)"). But even our updated code isn't infinite. After a while we'll overflow the 64-bit integer as well. So we should probably add some checks in the code for overflow and end the sequence. But I'll leave that as an exercise for you.

We've reached our goal which is to have an implementation of the Fibonacci Sequence with a set of valid tests. Now that we have this in place, we can do some experimentation with the implementation.

And with the tests in place, we'll know immediately if our experimentation breaks something. In an upcoming article, we'll look at those experiments. [Update: here's a bit of that experimentation.]

Happy Coding!