Dennis van der Stelt

The only way to win is to learn faster than anyone else

Community

Email Notifications

News

  • Addicted to Refactor! Pro

I read...

I Use...

Tags

Recent Posts

Archives

Blog Subscription Form

  • Email Notifications
    Go

September 2011 - Posts

How to unit test a method that has void as return type

How do I test a method that returns void? For example a method like this.

public void Process(int leadId)
{
    decimal price = _calculator.CalculateNormalLead(leadId);

    Lead lead = new Lead(leadId, price);

    _leadRepository.Save(lead);
}

How to test what the state of the lead object is? This is a question that comes up a lot of the time. It has many different answers. Some options:

  • Return a type anyway, even though you don’t use it. Or expose a property with the result. This are design changes that are only neccesary for testing. I’m sure that’s not what we want.
  • Check what the method changed, like records in the database. That’s a slow integration test.
  • Split the methods so that one portion of it returns something, and the second method just takes the result and uses it. This way however, you’ll have to make two calls to your class, which I think is even worse than the previous two options!
  • Expose internals by using the InternalsVisibleTo attribute. But now you have to make changes to the design of your class again, making private methods or properties internal.
  • Another option is to accept you can’t test everything. What you can however check is
    • Check if it throws exceptions
    • Check if it changes mutable objects you pass in as parameters
    • Do interaction based testing. Either write mocks or use a mocking framework to verify if calls were made to another method.

This last thing is something we can really use. Now this might not always work, but in our case it’s very usable. Imagine we have a repository that in production kind of works like this.

public class LeadRepository : ILeadRepository
{
    public void Save(Lead lead)
    {
        throw new NotImplementedException("Sorry, this is for an integration test.");
    }
}

Now I said kind of, because I don’t want to bother you with a lot of database and query stuff. But our lead enters this class and we’d really like to have this one! This repository implements an interface and the class is injected into the Invoicing class, where our Process method is. For the complete example, check the solution. But for now, check out this FakeRepository I created to use during testing.

public class FakeRepository : ILeadRepository
{
    public Lead CreatedLead { get; private set; }

    public void Save(Lead lead)
    {
        CreatedLead = lead;
    }
}

The only thing this repository does, is put the lead it receives into a property of the FakeRepository class. Remember that the repository in production does not have this property, so normally it’s never exposed.

Now we can write a test that injects our own fake objects and when asserting, we use the property of our FakeRepository to check the state of our lead.

[TestMethod]
public void Does_MapPropertiesCorrectly_When_CalculatingALead()
{
    // Arrange
    int leadId = 1;
    decimal leadPrice = 0.5m;
    FakeRepository fakeRepository = new FakeRepository();
    FakeCalculator calculator = new FakeCalculator(leadPrice);

    // Act
    Invoicing invoicing = new Invoicing(calculator, fakeRepository);
      invoicing.Process(leadId);

    // Assert
    Lead result = fakeRepository.CreatedLead;
    Assert.AreEqual(leadId, result.LeadId);
    Assert.AreEqual(leadPrice, result.Price);
}

See how easy it sometimes is? Download the complete solution here.

Locking in SQL Server and the nolock and readpast hints

I’ve written about transactions, the TransactionScope in .NET before.

Still I was recently surprised with the comment that I had to use the READPAST in my queries. So I started investigating and felt I had to blog about this. First of all, I’m not going to discuss every option here, just a few basics so you get what the differences are.  If you want to know  more, check out the BOL/MSDN documentation. Here’s a good starting point.

I’ve created a demo to show locking mechanisms. The database I used was the only one at hand at the time I wrote this, and it’s the Cookbook database from Dennis Doomen his Silverlight Cookbook example. It only has one table and a full select outputs the following result in my case.

cookbookdatabase

Shared Locks
First I want to tell about Shared Locks. Normally when you start a transaction like the code below, you place a shared lock until the transactions is over. A shared lock means someone else is still able to read the data you’ve locked, they just now allowed to update it. Because you want to be sure that the data you read, is still the same at the time you commit a transaction (to update data, for example).

SqlCommand cmd = new SqlCommand();
cmd.Connection = GetConnection();
cmd.CommandText = "select * from Recipes with (readpast)";
var reader = cmd.ExecuteReader();

The GetConnection() method initiates and opens a connection to my database.

ReadCommitted transaction
A regular transaction is done with isolation level ‘ReadCommitted’. This means that you cannot read data that has an exclusive lock on it. This is achieved when you update a row and another transaction tries to read it while you still have your transaction open. Here’s the update code.

TransactionOptions transactionOptions = new TransactionOptions();
transactionOptions.IsolationLevel = IsolationLevel.ReadCommitted;

using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew,transactionOptions))
{
    SqlConnection con = new SqlConnection();
    con.ConnectionString = ConnectionString;
    con.Open();

    SqlCommand cmd = new SqlCommand();
    cmd.Connection = con;
    cmd.CommandText = "update Recipes set Description = 'ChangedDescription' where Id = 1";                
    cmd.ExecuteNonQuery();
}

And here’s the code in another transaction that tries to read it.

SqlCommand cmd = new SqlCommand();
cmd.Connection = GetConnection();
cmd.CommandText = "select * from Recipes";
var reader = cmd.ExecuteReader();

while (reader.Read())
{
    Console.WriteLine("Description : {0}", reader["Description"].ToString());
}

The second method should not be able to retrieve data from the table until the transactions is closed. The original transaction can place multiple kinds of locks, including a lock that concerns the entire table. The select query I wrote just simply retrieves everything, and as the first row (at least) is locked, it’s not able to retrieve anything. If you add a where clause to filter out just the second row, it should be able to load the data anyway.

NOLOCK hint
So with the NOLOCK / READUNCOMMITTED hint/isolation-level, you tell your transaction (or query) that you want to read dirty data. Even though the transaction hasn’t been committed yet, you still want to read its data. A problem can occur that when the other transaction is rolled back, you’ve loaded data that isn’t there anymore. Worse yet, when SQL Server decides to shift data between different pages, you might simply loose a lot of data because it’s not there!

SqlCommand cmd = new SqlCommand();
cmd.Connection = GetConnection();
cmd.CommandText = "select * from Recipes with (nolock)";
var reader = cmd.ExecuteReader();

The result is that you get the change from the update statement. But because we never commit anything, everything is rolled back and we’ve read a dirty state.

Description : ChangedDescription
Description : Description2
Description : Description3
Description : Description4

READPAST hint
Now with the READPAST hint you’re telling SQL Server you do NOT want to read dirty data. Instead, it skips the rows that are locked.

SqlCommand cmd = new SqlCommand();
cmd.Connection = GetConnection();
cmd.CommandText = "select * from Recipes with (readpast)";
var reader = cmd.ExecuteReader();

Result is first row not being shown at all! So when we change the update query to ‘where Id = 1 or Id = 2’ we get one row less in the other select query.

Description : Description2
Description : Description3
Description : Description4

Conclusion
Be very, very aware of the problems you might introduce when using these table hints!

Download the demo solution here.

Value of Unit testing

There are probably uncountable amount of articles, posts and arguments on to why Unit Testing is so valuable. I just found a nice example that would’ve (or at least should’ve) been caught using Unit Tests. The following code was a bit adjusted to the need of this post, but has a mistake. See if you can spot it.

public class Discount
{
    public Discount(decimal percentage, DateTime startDate, DateTime endDate)
    {
        endDate.Add(new TimeSpan(23,59,59));

        Percentage = percentage;
        StartDate = startDate;
        EndDate = endDate;            
    }

    public decimal Percentage { get; private set; }
    public DateTime StartDate { get; private set; }
    public DateTime EndDate { get; private set; }
}

If you haven’t spotted it, I’d advise you to write a unit test to show that it’s failing. Here’s mine.
I’ve used FluentAssertions by the way, a library I love that helps me assert my code more easily.

[TestMethod]
public void Does_MapPropertiesCorrectly_When_CreatingNormalDiscount()
{
    // Act
    Discount discount = new Discount(5, new DateTime(2001, 9, 11), new DateTime(2011, 9, 11));

    // Assert
    discount.EndDate.Should().Be(new DateTime(2011, 9, 11, 23, 59, 59));
}

What is says is that it’s still 2011/9/11 00:00:00 instead of at the last second of that day. The problem here is that the endDate.Add method adheres to the framework guidelines. That is that you can’t edit an object you get passed by external source, only return new values. So the Add method returns a new DateTime object. We can make the test working with the following adjustment.

public class Discount
{
    public Discount(decimal percentage, DateTime startDate, DateTime endDate)
    {
        endDate = endDate.Add(new TimeSpan(23,59,59));

        Percentage = percentage;
        StartDate = startDate;
        EndDate = endDate;            
    }

    public decimal Percentage { get; private set; }
    public DateTime StartDate { get; private set; }
    public DateTime EndDate { get; private set; }
}

The only change I made was on line 5.