Kailayla
Kailayla

Reputation: 323

Unit test for my ViewModel?

I'm new to unit testing and I'm really stuck atm so I could really use some help.

Some application info
I have a WPF application in MVVM. It gets data from a database (classes generated via .edmx).
All linq queries are handled by methods in the Database class. In CustomerListViewModel, it makes a list of all Customers to be shown in the CustomerListView.

My problem
I am new to Unit Testing. I've read about it and tried to make it work. But as I understand, it should/can be done without touching the DB. I tried to find as much info as I could, but it wouldn't work with what I have. And now I'm basically stuck.

My question How do I unit test this piece of code? How can I know if I've successfully queried the database (with or without touching the DB in the unit test)?
(If I understand it for this piece, I can figure the rest of the classes and methods out on my own)

The code
CustomerListViewModel:

public CustomerListViewModel()
{
    MyObservableCollection<Customer> listCustomers = new MyObservableCollection<Customer>();
    ListCustomers = App.Database.GetCustomerList();
}

private void GetListCustomers()
{
    ListCustomers = App.Database.GetCustomerList();
    if (App.Database.hasError)
        App.Messenger.NotifyColleagues("SetStatus", App.Database.errorMessage);
}

Database:

public MyObservableCollection<Customer> GetCustomerList()
{
    hasError = false;
    MyObservableCollection<Customer> customerList = new MyObservableCollection<Customer>();
    try
    {
        QRM_Entities dc = new QRM_Entities();
        var query =
            from customers in dc.Customer
            select customers;

        foreach (Customer cust in query)
        {
            customerList.Add(cust);
        }
    }
    catch (Exception ex)
    {
        errorMessage = "GetCustomerList() error, " + ex.Message;
        hasError = true;
    }
    return customerList;
}

Upvotes: 1

Views: 4131

Answers (1)

Stephen Ross
Stephen Ross

Reputation: 892

The way that you have the ViewModel currently setup will make it almost impossible to unit test. The issue is on this line:

ListCustomers = App.Database.GetCustomerList();

I presume that App is a static and Database is the class that you are using as your Data Access Layer. So everytime that you call the constructor of your CustomerListViewModel you will call the actual Static implementation of App which you would have to setup before creating the View Model, meaning that you would always be testing with the actual Database, which is obviously what you are attempting to bypass.

In comes my favorite software principle the Dependency Inversion Principle, the premise of this is that decouple modules so that your high level module depends on an abstraction of a lower level module. And that details should depend on that abstraction. Effectively you should develop to an interface and provide this interface to dependents.

Taking your example I would extract interfaces for your database interaction and provide these to your View Model, but I'll go a step further and provide this to a model which will be provided to your view model.

IDatabase:

public interface IDatabase
{
    IEnumerable<ICustomer> GetCustomerList();
}

ICustomerListModel:

public interface ICustomerListModel
{
    ObservableCollection<ICustomer> Customers
    {
        get;
    }
}

CustomerListModel

public class CustomerListModel : ICustomerListModel
{
    private readonly IDatabase database;

    private readonly ObservableCollection<ICustomer> customers;

    public CustomerListModel(IDatabase database)
    {
        this.database = database;
        this.customers = new ObservableCollection(database.GetCustomerList());
    }

    public ObservableCollection<ICustomer> Customers
    {
        get
        {
            return this.customers;
        }
    }
}

CustomerListViewModel

public class CustomerListViewModel
{
    private readonly ICustomerListModel customerListModel;

    public CusomterListViewModel(ICustomerListModel customerListModel)
    {
        this.customerListModel = customerListModel;
    }

    public ObservableCollection<ICustomer> Customers
    {
        get
        {
            return this.customerListModel.Customers;
        }
    }
}

So what you can see here is that I have extracted an interface for the database which I request the information from, this means that I don't care about the implementation of the IDatabase, I just now that it provides me with a collection of ICustomer's when I call GetCustomerList().

So I inject a copy of the IDatabase into the CusomterListModel class which I can then query knowing that I'll get what I want back correctly. I then inject the ICustomerListModel into the ICustomerListViewModel so that the collection can be presented to the View.

So to test the CustomerListModel I would have a test like:

[Fact]
public void Customers_IsCorrectlyInitialisedAtStartup_Test()
{
    var databaseMock = new Mock<IDatabse>();
    var customer = new Customer();

    var customers = new [] { customer };

    databaseMock.Setup(mock => mock.GetCustomerList())
        .Returns(customers);

    var sut = new CustomerListModel(databaseMock.Object);

    Assert.Equal(customers, sut.Customers);
}

In this I have mocked a version of the IDatabase, now you can see how I don't care in the version of CustomerListModel what IDatabase I have as long as I can call GetCustomerList(). This has a setup to return a ICustomer when a call to GetCustomerList() is called. Finally I am asserting that the Customers collection was correctly populated with the returns of the IDatabase call.

Unit testing is a fine art, difficult to understand at first but when you get it working at first you'll pick it up quickly. Some things you may wish to look at to help you with generating unit testable code and actually testing:

  • Solid Principles, principles that every Software Engineer should at least be familiar with, will help generating code that is testable.
  • Dependency Injection, the wiki-page on Dependency Injection outlining the pros and cons to injecting the dependency into a constructor, when and how to use it in further examples.
  • Moq, a friendly and easy to use mocking framework for C#, which I used as part of my example above.

Upvotes: 6

Related Questions