Testing NHibernate Mappings

Several posts have been made lately concerning testing NHibernate Mappings. I thought I would share what I have done for the past few months.

I create a test fixture for each class mapped in NHibernate. The tests in the fixture test loading, updating, saving, and deleting through NHibernate.

Initially, I only tested loading, but later found out that when we started saving objects with NHibernate, it broke. Likewise, with deletes, I found some cascading deletes conflicted with triggers in the database (that I had no conrtol over). Had I tested the deletion, I would have known. So I find it a good practice to test all of the above for your mappings.

Here are some sample tests

[ TestMethod ]

public void Loading_a_VirtualColumn_will_populate_all_its_properties()

{

VirtualColumn expectedColumn = Anonymous.VirtualColumn();

// Insert column into Db.

_context.AddVirtualColumn( expectedColumn );

_context.Persist( _session );

// Load from Db.

VirtualColumn actualColumn = _session.Get<VirtualColumn>( expectedColumn.Id );

// Ensure all properties match.

TestAssertions.VirtualColumnEquals( expectedColumn, actualColumn );

}

[ TestMethod ]

public void Updating_a_VirtualColumns_CriteriaExpression_and_saving_should_persist_the_change()

{

VirtualColumn column = Anonymous.VirtualColumn_WithVersionCriteria();

// Insert column with its criteria expression into Db.

_context.AddVirtualColumn( column );

_context.Persist( _session );

// Get column to update from db.

VirtualColumn persistedColumn = _session.Get<VirtualColumn>( column.Id );

Assert.IsInstanceOfType( persistedColumn.CriteriaExpression,

typeof ( VersionExpression ) );

// Update column and persist.

persistedColumn.CriteriaExpression = Anonymous.BudgetYearExpression();

_session.SaveOrUpdate( persistedColumn );

_session.Flush();

// Get the updated column.

VirtualColumn actualColumn = _session.Get<VirtualColumn>( column.Id );

// Ensure the change was persisted.

Assert.IsInstanceOfType( actualColumn.CriteriaExpression,

typeof ( BudgetYearExpression ) );

}

[ TestMethod ]

public void Deleting_a_Virtual_Column_should_remove_it_from_the_db()

{

VirtualColumn column = Anonymous.VirtualColumn();

// Insert column into Db.

_context.AddVirtualColumn( column );

_context.Persist( _session );

// Get column to delete from db.

VirtualColumn persistedColumn = _session.Get<VirtualColumn>( column.Id );

// Delete the column.

_session.Delete( persistedColumn );

_session.Flush();

// Ensure it was removed from db.

TestDataAssertions.VirtualColumnDoesNotExist( column, _session );

}

[ TestMethod ]

public void Deleting_a_Virtual_Column_should_also_remove_its_Criteria_Expression_from_the_db()

{

VirtualColumn column = Anonymous.VirtualColumn_ForAmountInPeriod( January );

// Insert column into Db.

_context.AddVirtualColumn( column );

_context.Persist( _session );

// Get column to delete from db.

VirtualColumn persistedColumn = _session.Get<VirtualColumn>( column.Id );

// Delete the column.

_session.Delete( persistedColumn );

_session.Flush();

// Ensure it was removed from db.

TestDataAssertions.ExpressionDoesNotExist( column.CriteriaExpression, _session );

}

The code may require some explanation, because much of the nuts and bolts functionality is hidden behind classes that make the tests much more readable. Let me explain what these do:

The _context object is a DynamicConext class that I created for unit testing that allows me to easily create test object graphs, and it also provides the ability to insert the data from those objects into a database. It uses the NHibernate _session object to do so, but doesn’t use the NHibernate mappings.

The Anonymous object is an object mother I created to create anonymous test objects. It simply returns a valid object with random data.

The TestAssertions and TestDataAssertions classes are custom assertions I wrote to increase readability of the tests. The TestAssertions assert against objects whereas the TestDataAssertions assert against a database. This is so that I can test the database state independently from NHibernate. (It’s not a good idea to use NHibernate to test NHibernate).

Note that the tests do not use any custom repositories from my domain. They just use the NHibernate session directly. This is because I am not testing my repositories. I only want to test my NHibernate mappings. If I had a custom repository for the “CalculatedField” class, I would test it separately.

In upcoming posts I will explain my implementation of the DynamicContext, Anonymous, and custom TestAssertion classes.

-=CE=-

Posted in Agile, Test-Driven Development, Testing | Tagged , , | Leave a comment

Don’t Forget the Acceptance Tests!

Our tester recently informed me that several stories we had completed in a previous iteration were failing tests. The functionality simply was not working. To make matters worse, at least one of the stories was actually coded and marked complete by me! I was dumbfounded. How could I mark a story complete when it was not done?

After pondering the situation for a while, I discerned at least one problem: We had no acceptance tests to define when a story was complete. Therefore, there was no failing test telling something wasn’t working. As a team we quickly implemented a two-fold policy: (1) No code may be written for a story until we first define its acceptance criteria, and (2) A story may not be considered complete until all its acceptance tests pass.

How well has it worked? Well, we have not had any missed requirements since implementing these policies. And our tester is thoroughly impressed with the increase in code quality.  Furthermore, I have found that the process of coming up with the acceptance tests, gives us a much better understanding of the user story. We better understand the customer’s expectations, how they will use the functionality we are implementing, and why they need it. This helps us arrive at a more correct and complete design.  It also helps increase the accuracy of our estimates since there are fewer unknowns, and more information. All in all, the introduction of acceptance criteria into our process has had an extremely positive affect on the project.

So, don’t forget those acceptance tests!

-=CE=-

Posted in Agile, Testing | Tagged , , | Leave a comment

Agility Depends on Good Design and Complete Test Coverage

I have a very different approach to TDD. On my project, we hold a group design session before we write any tests or code. In that design session, we determine the object structure and bombard our design with edge cases and such. We continue modifying it on the whiteboard till it satisfies all the requirements. After we have the design, we enumerate all the tests that will ensure the implementation works as expected (considering our user requirements as well). We actually end up with a list of test names that need to be implemented.

This is one of the hardest parts of our design session. Its so easy to say…it works…just implement it. But it is the most important part of our design…proving that it works. Many times we come up with tests that bring to light issues with our design that force us to tweak it. This list of tests is absolutely invaluable when you implement the code to ensure your design does what it should.

It is only after we have the detailed design and a list of tests that prove it work that we actually begin implementation. We implement it using TDD. We write each test one at a time and then implement the code that will make it pass. When the last test has been implemented and passes, the task is done…

If we wrote the tests last, we probably would skip them, and we wouldn’t have a clear path of what to do. Our tests provide that…they give us a goal to accomplish before moving on to the next one. (We order our tests during the design session in the order they should be implemented).

I realize this is a hybrid approach and differs from purist form of TDD where TDD itself is used for design discovery. We separate our design and I find that we spend more time in design than we do coding. Writing an application is far more than just typing in the code. Typing is the easy part…I leverage the whole team for the hard part…design. The code is usually implemented in no time…as long as the design was correct. In fact, typing in the code is almost an afterthought.

TDD is invaluable to this design and implement process. Without the tests, we cannot make huge sweeping design changes in the code (which we do quite often). Refactoring tools go a long way to help as well.

The tests are the primary key to having an “Agile” codebase (one that can be easily modified to accommodate changing requirements).

In the absence of tests, you can’t change code without having the confidence it will work after the change. Tests give you that confidence. If they pass, your code will work… If they don’t, you can just roll back the changes (worst case) or fix the code so they do pass.

In the absence of Tests, changes to design are painful because you break things. You will still break code when you have tests, but you immediately know that its broke, what broke it, and where it broke. (because you just did it). If you wait a month before a tester tells you it broke, you have no idea where to start fixing it.

The design is another key to having an agile codebase… While tests ensure your changes were successful, Design allows you to make these changes easily.

In the absence of well-designed code, changes are difficult. If you don’t have a solid design to work from, (I am talking about the codebase you are working with here..) with your concerns consolidated and cleanly separated, you can’t change things quickly. The codebase must be very clean and well designed for you to truly reap the benefits of agile.

In the absence of well-designed code, TDD (or testing in general) is truly painful. You may not even be able to test a lot of your code because it wasn’t designed to be testable.

In the absence of well-designed code, further design is very difficult: You can’t “get your head around” the design. You try to consider all the things that need to be “updated” when you make a change but its too difficult because your concerns are scattered all over the system. Things aren’t simplified and consolidated, so you can’t make one change in one place…its many changes everywhere…and until you implement it and see it break, you may not even know where those changes are needed.

Maybe I am rambling, maybe I am off topic… I am just passionate about this because just came from an well-designed app built from the ground up with TDD. We had great success…design and testing were simple. We could make huge design changes in no time to adapt the application to any change they threw our way. I was blown away by our productivity. I am now on a project that was under-designed, and had very little test coverage. TDD and design were VERY painful when I came onboard. If I had not had such an awsome experience with TDD on the prior project, I would have given up long ago. However, I stuck with it. We have been at it for 4 months. The project’s codebase is now 95% covered in tests, and has been 80% redesigned and it is going so much smoother. We are finally making great progress. I am now finding that the mere 20% of the codebase that is still poorly designed is where we have all our pain. Everything else is a simple to work with.

The bottom line? If you think TDD is not working…your codebase might be all out of whack. If you have good design, and complete test coverage, Design and testing will be a breeze and you will reap the benefits of it. Otherwise, it may be a long slow (and painful) road to get your codebase where you can see the benefits. It has taken us 4 months of pain so far to get to where we are now. We are now seeing the light at the end of the tunnel and I can honestly say it was worth every second of it!

-=CE=-

Posted in Agile, Design, Test-Driven Development, Testing | Tagged , , , | Leave a comment

Importance of Treating Unit Tests as First Class Code

On my previous project, we had a rather large set of unit tests. This collection of tests grew over time and we neglected to maintain them with the fervency in which we maintained our production code. This quickly came back to haunt us.

Making sweeping changes to our codebase became painful because our tests were too difficult to modify. Our test project was weighing us down with code debt.

Always remember that you are only as agile as the weakest code in your codebase. You must maintain your tests as if they were just as important as your production code…because they are!

We remedied the problem by refactoring our test project extensively, but we should not have had to do so. If only we had been diligent in keeping that codebase well designed and clean from the start… Don’t make the same mistake.

-=CE=-

Posted in Agile, Principles, Test-Driven Development, Testing | Tagged , , , | Leave a comment

TDD: Is -Just Make It Pass- Too Mundane?

In Test-Driven Development, I used to think it was a waste of time to write only enough code to make a test pass. I felt that if the code was not completely correct, why should I write it? Now, I feel differently.

Some of the examples you will encounter while learning about TDD may seem downright ridiculous to you…they did to me. These examples often promote the idea that I should write a test, then write the code to simply return the expected value so the test will pass. This seems pointless since it is obvious that the code is wrong, but the test passes. Why would I want to do that? For quite sometime I just didn’t get it, but now I do.

The reason you want to get the test to pass is to enable you to continue writing another test for the functionality that you just faked. And to do so without having other tests failing. The concept is called “Fake it ’till you make it”. Take for example the classic bowling game example. The first test is usually:

[Test]
public void A_game_of all_gutter_balls_should_have_a_score_of_zero()
{
    BowlingGame game = new BowlingGame();
    for ( int i = 0; i <= 10; i++ )
        game.throw( 0 );

    Assert.AreEqual( 0, game.Score );
}

If we followed the rule of only making the simplest thing possible that will work, we would write the BowlingGame as follows:

public class BowlingGame
{
    public int Score
    {
        get { return 0; }
    }
}

This is all that is needed to actually make the test pass and we can move on. Why is it ok to do this, knowing that return 0; is not the final correct implementation? Its ok because this is an intermediate step. You should soon remedy this situation by writing more tests.

In fact, having all your tests pass, but seeing that the implementation is not correct is a code smell that indicates you need more tests to fully specify the functionality you are missing. If you know the code is incomplete, yet all your unit tests pass, you know that the functionality that is missing from your code (that makes it incomplete) is missing tests to specify and validate its behavior.

Write a test to make the current implementation fail and specify the missing behavior so you can write the code to implement it. If you can’t conceive of a new failing unit test, then perhaps the current implementation is correct after all, or perhaps you are trying to over-design the code when a more simpler solution will suffice.

-=CE=-

Posted in Agile, Test-Driven Development, Testing | Tagged , , | Leave a comment