views:

41

answers:

3

I am embarking upon my first journey of test driven development in C#. To get started I'm using MSTest and Rhino.Mocks. I am attempting to write my first unit tests against my ICustomerRepository. It seems tedious to new up a Customer for each test method. In ruby-on-rails I'd create a seed file and load the customer for each test. It seems logical that I could put this boiler plate Customer into a property of the test class but then I would run the risk of it being modified. What are my options for simplifying this code?

[TestMethod]
public class CustomerTests : TestClassBase
{
    [TestMethod]
    public void CanGetCustomerById()
    {
        // arrange
        var customer = new Customer()
        {
            CustId = 5,
            DifId = "55",
            CustLookupName = "The Dude",
            LoginList = new[] {
                new Login { LoginCustId = 5, LoginName = "tdude" } }
        };

        var repository = Stub<ICustomerRepository>();

        // act
        repository.Stub(rep => rep.GetById(5)).Return(customer);

        // assert
        Assert.AreEqual(customer, repository.GetById(5));
    }

    [TestMethod]
    public void CanGetCustomerByDifId()
    {
        // arrange
        var customer = new Customer()
        {
            CustId = 5,
            DifId = "55",
            CustLookupName = "The Dude",
            LoginList = new[] {
                new Login { LoginCustId = 5, LoginName = "tdude" } }
        };

        var repository = Stub<ICustomerRepository>();

        // act
        repository.Stub(rep => rep.GetCustomerByDifID("55")).Return(customer);

        // assert
        Assert.AreEqual(customer, repository.GetCustomerByDifID("55"));
    }

    [TestMethod]
    public void CanGetCustomerByLogin()
    {
        // arrange
        var customer = new Customer()
        {
            CustId = 5,
            DifId = "55",
            CustLookupName = "The Dude",
            LoginList = new[] {
                new Login { LoginCustId = 5, LoginName = "tdude" } }
        };

        var repository = Stub<ICustomerRepository>();

        // act
        repository.Stub(rep =>
            rep.GetCustomerByLogin("tdude")).Return(customer);

        // assert
        Assert.AreEqual(customer, repository.GetCustomerByLogin("tdude"));
    }
}

Test Base Class

public class TestClassBase
{
    protected T Stub<T>() where T : class
    {
        return MockRepository.GenerateStub<T>();
    }
}

ICustomerRepository and IRepository

public interface ICustomerRepository : IRepository<Customer>
{
    IList<Customer> FindCustomers(string q);
    Customer GetCustomerByDifID(string difId);
    Customer GetCustomerByLogin(string loginName);
}

public interface IRepository<T>
{
    void Save(T entity);
    void Save(List<T> entity);
    bool Save(T entity, out string message);
    void Delete(T entity);
    T GetById(int id);
    ICollection<T> FindAll();
}
+2  A: 

I would extract the code to create the customer into a method. You can call that from your test methods, and if you name it something like SetUpCustomerForRepository then it will provide some documentation about what you are doing.

Here is an example based on your sample:

 private Customer SetUpCustomerForRepository()
 {
    return new Customer()
    {
        CustId = 5,
        DifId = "55",
        CustLookupName = "The Dude",
        LoginList = new[] {
            new Login { LoginCustId = 5, LoginName = "tdude" } }
    };
 }

You could also call it from the test set up method, but I actually prefer to do it in the test method so that people looking at the test know what is being set up for the test.

As for your concern about it being modified....if that happens, then your tests will fail, and you'll know about it.

ckramer
I think I know what you are saying but would you mind putting it into code for the sake of posterity? :)
ahsteele
Added the method to create a customer, then noticed that your tests are using different conditions for the stub in Rhino Mocks, so it's probably best to leave those in the test (that way it is easier to see what you are testing).
ckramer
+2  A: 

You could immunize yourself against changes to your test customer by modifying your stubs and assertions to use its properties:

[TestMethod]
public void CanGetCustomerByDifId() {

    var customer = this.TestCustomer;

    var repository = Stub<ICustomerRepository>();

    repository.Stub(rep => rep.GetCustomerByDifID(customer.DifID))
              .Return(customer);

    Assert.AreEqual(customer, repository.GetCustomerByDifID(customer.DifID));
}
Jeff Sternal
+3  A: 

Your unit tests don't have any real value because they're just testing Rhino.Mocks.

What you want to do is use Rhino.Mocks to create an ICustomerRepository stub for other components/services that need an ICustomerRepository. This way, instead of using a CustomerRepository that talks to a database or some flat file, you'll use the stubbed ICustomerRepository and Rhino.Mocks will let you set up pre-defined responses to certain methods, thus allowing you to test your services in isolation.

Patrick Steele
+1 - nicely put.
Jeff Sternal