Skip to content

Instantly share code, notes, and snippets.

@bryzaguy
Last active August 29, 2015 13:58
Show Gist options
  • Save bryzaguy/10418378 to your computer and use it in GitHub Desktop.
Save bryzaguy/10418378 to your computer and use it in GitHub Desktop.

#Patterns DAWG

##Methodologies

The methodologies used when coding make a big impact on the resulting code. The two most important methodologies to prescribed for high quality and maintainable code are Test Driven Development and Refactoring.

###Test Driven Development (TDD)

Writing tests before production code sounds simple but changes the resulting code and resulting architecture.

Three easy to remember but hard to do steps.

Red > Green > Refactor

  1. Write a failing test.
  2. Make the test pass.
  3. Refactor code to remove code smells.

####Separating Load, Bind and Execute

By arranging code so that loading (e.g. getting from a database), binding (mapping or transforming) and executing are separated it is possible to eliminate the need for Mocks in unit testing.

Consider what you would have to do to test the Place method in this example:

public class Order
{
    public bool Success = false;
    public Money Amount { get; set; }
    private readonly IConfigReader _configReader;
    
    public Order(IConfigReader configReader)
    {
        _configReader = configReader;
    }
    
    public void Place() // This is responsible for both Loading and Executing.
    {
        var minimumOrderAmount = _configReader.GetMinimumOrderAmount() < 10 
            ? _configReader.GetMinimumOrderAmount() : new Money(20);
        
        if (minimumOrderAmount > Amount)
        {
            Success = true;
        }
    }
}

Without altering the code for Order, to test the Place method we would need Mock the config reader and its GetMinimumOrderAmount behavior. It would look something like this.

[Test]
public void WhenOrderPlacedAtMinimumAmountThenSuccess()
{
    var amount = new Money(15);
    var mockedConfigReader = new Mock<IConfigReader>();
    mockedConfigReader.Setup(a => a.GetMinimumOrderAmount()).Returns(amount);
    var order = new Order(mockedConfigReader.Object) { Amount = amount };
    
    order.Place();
    
    Assert.IsTrue(order.Success);
}

1. Isolating Execute

Taking a closer look, notice the config reader is only used to load and bind the minimumOrderAmount value. And we are really only using the mock to isolate the functionality we want to test. If we changed what loads the minimumOrderAmount we might break the test so this is a brittle test. We can achieve better isolation and a more stable test by taking the config reader out and passing in what we need from the config.

public class Order
{
    public bool Success = false;
    public Money Amount { get; set; }
    private readonly Money _minimumOrderAmount;
    
    public Order(Money minimumOrderAmount)
    {
        _minimumOrderAmount = minimumOrderAmount;
    }
    
    public void Place()
    {
        if (_minimumOrderAmount > Amount)
        {
            Success = true;
        }
    }
}

...

[Test]
public void WhenOrderPlacedAtMinimumAmountThenSuccess()
{
    var amount = new Money(15);
    var order = new Order(amount) { Amount = amount };
    
    order.Place();
    
    Assert.IsTrue(order.Success);
}

Now that the config reader is removed say we want to test the minimum value binding logic. Pretend we moved that code to a new class that loads and binds. Here's my new test.

[Test]
public void MinimumValueIsNotSetToDefaultWhenOverTen()
{
    var amount = new Money(15);
    var mockedConfigReader = new Mock<IConfigReader>();
    mockedConfigReader.Setup(a => a.GetMinimumOrderAmount()).Returns(amount);
    var binder = new MinimumValueBinder(mockedConfigReader.Object);
    
    var result = binder.GetMinimumOrderAmountOrDefault();
    
    Assert.AreEqual(amount, result);
}

2. Isolating Bind

To clean this up we need to remove the reader from the binding class. Here's the updated test.

public void MinimumValueIsNotSetToDefaultWhenOverTen()
{
    var amount = new Money(15);
    var binder = new MinimumValueBinder(amount);
    
    var result = binder.GetMinimumOrderAmountOrDefault();
    
    Assert.AreEqual(amount, result);
}

3. Testing Load

Testing load is generally done with integration tests. The good news, however, is that by separating the bind and execute portions of the code most scenarios are now easy to unit test without any Mocking necessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment