views:

112

answers:

4

I am unit testing an ICustomerRepository interface used for retrieving objects of type Customer.

  • As a unit test what value am I gaining by testing the ICustomerRepository in this manner?
  • Under what conditions would the below test fail?
  • For tests of this nature is it advisable to do tests that I know should fail? i.e. look for id 4 when I know I've only placed 5 in the repository

I am probably missing something obvious but it seems the integration tests of the class that implements ICustomerRepository will be of more value.

[TestClass]
public class CustomerTests : TestClassBase
{
    private Customer SetUpCustomerForRepository()
    {
        return new Customer()
        {
            CustId = 5,
            DifId = "55",
            CustLookupName = "The Dude",
            LoginList = new[]
            {
                new Login { LoginCustId = 5, LoginName = "tdude" },
                new Login { LoginCustId = 5, LoginName = "tdude2" }
            }
        };
    }

    [TestMethod]
    public void CanGetCustomerById()
    {
        // arrange
        var customer = SetUpCustomerForRepository();
        var repository = Stub<ICustomerRepository>();

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

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

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: 

Interfaces, by definition, are just contracts, so there's no code to test. You want to write unit tests against the concrete implementation(s) of the interface, because that is where the actual execution code lives.

If there's no concrete implementation of the interface and you're just mocking a concrete implementation, then your unit test is just based on a mock. That isn't really of much value.

dcp
+5  A: 

I may be missing something but it seems like every single aspect of your test is mocked up?

Generally speaking you only mock up the objects that aren't core to the test. In this case you may use this repository as the source for a function which you expect to do something with the repository to retrieve customer #5 and perform an operation on it.

For example you may mock up a customer repository so that you can call a method that verifies the login of a user. You'd use your mock repository to prevent your unit-test from relying on a real data-source, not to test your mock repository.

Tim Schneider
+1  A: 

Your test makes no sense to me. You are testing if a preprogrammed stub is returning the values you feed it, You are not testing any real code.

Follow dcp's reply. An interface is a declaration of methods. You should test implementations of these.

Carles Barrobés
+4  A: 

Rule #1 of testing:

Know what the purpose of your test is and what it is trying to prove before you write it. If you don't know what it proves, then it's useless :)

As the other posters have correctly said, you're stubbing an interface and then calling the stub -- this doesn't prove anything about whether your production code works.

What is a stub?

Stubs are used to provide canned values to drive some aspect of the class under test. E.g. Say you have a CustomerService that has an instance of type ICustomerRepository. If you wanted to see that the CustomerService could gracefully handle an error case when the repository was empty, you would stub the ICustomerRepository's GetCustomerById method to return nothing/null/throw an exception, then ensure that the CustomerService method did the correct thing (e.g. return a customer not found result).

I.e. the stub is just a collaborator that helps you reach the particular condition/behaviour of interest. We're testing CustomerService and the stubbed ICustomerRepository merely helps us achieve our goal.

You're not the first to ask this very question :). I usually advise developers to hand-roll their test doubles at first. It helps to understand all of the interactions and what the framework is actually doing for you.

Mark Simpson