Thursday, 28 February 2013

Checking Parameter Passed to a Method with Moq

Say we are using TDD and would like to add more business logic to the method below. We would like to make sure that the object's created and updated time should be set with current time.
public void Add(Item item)
{        
    // set item created time

    // set item updated time 

    repository.Add(item);
}

Moq provides some argument checking methods that we can use; there are:
- It.Is<Type>(...) ,
- It.IsInRange<Type>(...) and
- It.IsRegex(...)

Below is a unit test using one of the methods; It.Is<Type>(o => checking_condition)
[Fact]
public void Created_time_should_be_set_as_current_time()
{
    // Arrange
    var item = new Item(); 
    var timeToCompare = DateTime.Now.AddHours(-1);

    // Act
    itemDS.Add(item);

    // Assert
    repository.Verify(c => c.Add(It.Is<Item>(i => i.CreatedAt.CompareTo(timeToCompare) > 0)));
}
or we can write like this:
[Fact]
public void Created_time_should_be_set_as_current_time()
{
    // Arrange
    var item = new Item(); 
    var timeToCompare = DateTime.Now.AddHours(-1);
    repository.Setup(c => c.Add(It.Is<Item>(i => i.CreatedAt.CompareTo(timeToCompare) > 0)));

    // Act
    itemDS.Add(item);

    // Assert
    repository.VerifyAll();
}
Run the test. It will fail. Then we can put the code item.CreatedAt = DateTime.Now; on the method. Run the test again. It should pass now.

Now we want to create a test to make sure updated time is set. We will use another way to check the parameter passed to a method. We will use Callback feature to retrieve the parameter. We cannot put a checking conditional logic in Callback argument. However we can assign the passed parameter to an existing object or add it to an existing collection then later we can inspect it.

[Fact]
public void Updated_time_should_be_set_as_current_time()
{
    // Arrange
    var item = new Item(); 
    var timeToCompare = DateTime.Now.AddHours(-1);

    // repository.Setup(c => c.Add(It.Is<Item>(i => i.UpdatedAt.CompareTo(timeToCompare) > 0)));
    // alternate way by using Callback
    repository.Setup(c => c.Add(It.IsAny<Item>())).Callback<Item>(i => item = i);

    // Act
    itemDS.Add(item);

    // Assert
    // repository.VerifyAll();
    // now we are using Callback
    Assert.True(item.UpdatedAt.CompareTo(timeToCompare) > 0);
}

Just for a note, if required, Callback can be combined with Returns function as well. For example:
mock.Setup(. . .)
    .Returns(. . .)
    .Callback(. . .)

mock.Setup(. . .)
    .Callback(. . .)
    .Returns(. . .)
    .Callback(. . .)

Run the test. It will fail. Put the code item.UpdatedAt = DateTime.Now; Now the test will pass.

Our updated method now is
public void Add(Item item)
{        
    // set item created time
    item.CreatedAt = DateTime.Now;

    // set item updated time 
    item.UpdatedAt = DateTime.Now;

    repository.Add(item);
}

References:
http://code.google.com/p/moq/wiki/QuickStart
http://stackoverflow.com/questions/3269717/moq-how-to-get-to-a-parameter-passed-to-a-method-of-a-mocked-service

Thursday, 21 February 2013

How to Seed/Initialise Some Data in Entity Framework Code First

Entity Framework version 5.0.0 is used when writing this post.

First, we need to create a class derived from one of the built in database initialiser options' classes. They are CreateDatabaseIfNotExists, DropCreateDatabaseIfModelChanges and DropCreateDatabaseAlways. In the example below is DropCreateDatabaseIfModelChanges. I think it is possible to create a custom initialiser if we wish to do so.
public class DBInitialiser : DropCreateDatabaseIfModelChanges<MyContext>
{
    protected override void Seed(MyContext context)
    {
        // populate sizes table
        var sizes = new List<Size> {
            new Size {SizeId = 1, Code="S", Description="Small"},
            new Size {SizeId = 2, Code="M", Description="Medium"},
            new Size {SizeId = 3, Code="L", Description="Large"}
        };
        sizes.ForEach(s => context.Sizes.Add(s));       
    }
}
Notice that on line 1, we also pass the context type. Then we need to override the Seed() method and create some items to populate the database.

Next, we need to call Database.SetInitializer() method with an instance of the new derived class as its parameter when the application starts. In an MVC application, this would be inside Application_Start() in Global.asax.cs file.

If you have used Database.SetInitializer() with one of the built in database initialiser options as its parameter then you need to replace it with the new derived class' instance.
//Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MyContext>());
    // Replaced the parameter with the new derived class' instance
    Database.SetInitializer<MyContext>(new DBInitialiser());