iterative context specification code

In my last few posts I've described the process of a basic implementation of a user story that has resulted in a working system that conforms to the specification laid down by test code. You can catch up on the series here.

In this post I'm now going to look at addressing a second user story and showing a process of iterative development of the context specification code itself - if you find something that has a code smell or doesn't look right, bear with the post as it may very well be addressed later on; I'm purposefully showing the code all its forms from inception to finished version. If there's still something that isn't right then please let me know as I'd love some feedback on this process.
The user story detail is as follows:

"As a user I want to input a new balance with an amount and for a particular date, defaulting to today's date if a date is not supplied."

This is going to sit on the same Balance view as the previous story so I start with the BalancePresenter and create a context for this where the input is validated and the appropriate action taken on the view:

[Observations] 
public class when_saving_a_new_balance : BalancePresenterSpecification 
{ 
    [Observation] 
    public void should_call_the_view_for_new_balance_validity() 
    { 
        sut.SaveCurrentBalance(); 
        balanceView.was_told_to(v => v.NewBalanceIsValid()); 
    } 

    [Observation] 
    public void should_call_the_view_to_obtain_new_balance_amount_when_new_balance_is_valid() 
    { 
        balanceView.Stub(v => v.NewBalanceIsValid()).Return(true); 
        sut.SaveCurrentBalance(); 
        balanceView.was_told_to(v => v.GetNewBalanceAmount()); 
    } 
}

This context inherits from the base class I've used in previous specifications and in the first observation I'm asking the view if the balance is valid - at this stage I know this isn't the right location for business logic, but in order for me to slowly introduce my context I find this a suitable place to control the logic for the time being. I'm also combining both tests in one illustration here for brevity when I would normally implement one at a time.
I'm also observing that when the balance is valid that the new balance amount is retrieved from the view - with placeholder code put in for the new sut.SaveCurrentBalance() method, this test fails so I now implement the code in the BalancePresenter class:

public void SaveCurrentBalance() 
{ 
    balanceView.GetNewBalanceAmount(); 
}

The test now passed and I can move onto an observation that evaluates that when the input is invalid the view new balance is not obtained:

[Observations] 
public class when_saving_a_new_balance : BalancePresenterSpecification 
{ 
    [Observation] 
    public void should_call_the_view_for_new_balance_validity() 
    { 
        sut.SaveCurrentBalance(); 
        balanceView.was_told_to(v => v.NewBalanceIsValid()); 
    } 

    [Observation] 
    public void should_call_the_view_to_obtain_new_balance_amount_when_new_balance_is_valid() 
    { 
        balanceView.Stub(v => v.NewBalanceIsValid()).Return(true); 
        sut.SaveCurrentBalance(); 
        balanceView.was_told_to(v => v.GetNewBalanceAmount()); 
    } 

    [Observation] 
    public void should_not_call_the_view_to_obtain_new_balance_amount_when_balance_is_invalid() 
    { 
        balanceView.Stub(v => v.NewBalanceIsValid()).Return(false); 
        sut.SaveCurrentBalance(); 
        balanceView.was_never_told_to(v => v.GetNewBalanceAmount()); 
    }  
}

Note that in both cases the stubbing is now more noticeable as being part of the observation method itself prior to the assertion - this looks like a code smell as I'm trying to write all observations as only having the Assert part of the Arrange-Act-Assert process (and not contain a mixture of arrange and assert) so this is something that I want to address but for now I want to implement the code to get these tests to pass.
In the balance presenter I implement as follows to get my tests to pass.

public void SaveCurrentBalance() 
{ 
    bool newBalanceIsValid = balanceView.NewBalanceIsValid(); 
    if (newBalanceIsValid) 
        balanceView.GetNewBalanceAmount(); 
}

I now want to put in some new observations checking that the balance date is also obtained. When I come to put the code into the context specification I can immediately see that I will need to put a positive version and a negative version as I have done in the previous example and also include the stubs in these new observations. This is starting to really smell now so stopping to think I recognise that I actually have two different contexts... one when the balance input is valid and one when invalid. So I go about refactoring these tests:

[Observations] 
public class when_saving_a_new_balance_but_balance_input_is_invalid : 
                                BalancePresenterSpecification 
{ 
    private context c = () => balanceView.Stub(v => v.NewBalanceIsValid()).Return(false); 

    because b = () => sut.SaveCurrentBalance(); 

    [Observation] 
    public void should_call_the_view_for_new_balance_validity() 
    {    
        balanceView.was_told_to(v => v.NewBalanceIsValid()); 
    } 

     [Observation] 
    public void should_not_call_the_view_to_obtain_new_balance_amount() 
    { 
        balanceView.was_never_told_to(v => v.GetNewBalanceAmount()); 
    } 
} 
[Observations] 
public class when_saving_a_new_balance_and_balance_input_is_valid : 
                                BalancePresenterSpecification 
{ 
    private context c = () => balanceView.Stub(v => v.NewBalanceIsValid()).Return(true); 

    because b = () => sut.SaveCurrentBalance(); 

    [Observation] 
    public void should_call_the_view_for_new_balance_validity() 
    {    
        balanceView.was_told_to(v => v.NewBalanceIsValid()); 
    } 

    [Observation] 
    public void should_call_the_view_to_obtain_new_balance_amount() 
    { 
        balanceView.was_told_to(v => v.GetNewBalanceAmount()); 
    } 
}

With the two new contexts things are certainly a lot clearer and we can put the rest of the specifications and observations in the right location. However the code above still has some repetition... the observation that the validity is obtained from the view is in both. With some more rework I end up with the following:

[Observations] 
public class when_saving_a_new_balance_specification : BalancePresenterSpecification 
{ 
    because b = () => sut.SaveCurrentBalance(); 

    [Observation] 
    public void should_check_for_new_balance_validity_on_the_view() 
    { 
        balanceView.was_told_to(v => v.NewBalanceIsValid()); 
    } 
}

[Observations] 
public class when_saving_a_new_balance_but_balance_input_is_invalid : 
                                BalancePresenterSpecification 
{ 
    private context c = () => balanceView.Stub(v => v.NewBalanceIsValid()).Return(false); 

    because b = () => sut.SaveCurrentBalance(); 

     [Observation] 
    public void should_not_call_the_view_to_obtain_new_balance_amount() 
    { 
        balanceView.was_never_told_to(v => v.GetNewBalanceAmount()); 
    } 
} 

[Observations] 
public class when_saving_a_new_balance_and_balance_input_is_valid : 
                                BalancePresenterSpecification 
{ 
    private context c = () => balanceView.Stub(v => v.NewBalanceIsValid()).Return(true); 

    because b = () => sut.SaveCurrentBalance(); 

    [Observation] 
    public void should_call_the_view_to_obtain_new_balance_amount() 
    { 
        balanceView.was_told_to(v => v.GetNewBalanceAmount()); 
    } 
}

With an additional context that is just about generally saving the current balance I now have a clear separation of code and can continue on with adding my specifications.
My next post in this series will dig into this deeper - please bear in mind that there are still things with the code illustrated in this post that I want to address and it is not necessarily the finished product.



0 comments: