Was this page helpful?
Your feedback about this content is important. Let us know what you think.
Additional feedback?
1500 characters remaining
Export (0) Print
Expand All

How to: Unit Test a Presenter

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies.
This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

When you implement the Model-View-Presenter pattern, you can test the presenter independently of the view and model concrete implementations. To do this, replace the view and the model with mock implementations. Testing the presenter independently of the model and view implementations helps you to focus on the presentation behavior and eliminates dependencies that might not be appropriate for testing environments.

If the presenter references its view implementation class, then you need the view implementation class to run the presenter’s unit tests. To more easily test your presenter, define an interface for the view and replace the reference to the view implementation with a reference to the view interface. This means you can create a mock implementation of your view for your presenter’s unit tests, and those tests will not require the real view implementation. The same approach can be applied to any model objects that the presenter requires.

Steps

The typical process for unit testing a presenter includes the following tasks:

  1. Create mock implementations for the model and the view. These mock implementations must implement the model and view interfaces.
  2. For each user story (a unit of customer-visible functionality), identify and add members to the view and model interfaces and implement them in the mock implementations. Write a unit test using the mock objects that perform assertions on the state of the mock objects. Finally, add code to the presenter to make the test pass.

The following procedure describes how to create a mock view.

To create a mock view

  1. Define a new class that implements the view's interface.
    Ff709846.note(en-us,PandP.10).gifNote:
    Note: How to: Add a Page with a Presenter, How to: Add a Master Page with a Presenter, and How to: Add a User Control with a Presenter recipes included in the Developing Web Client Applications guidance package automatically generate a mock view class if you have a test project for the business module on which the recipe is executed. The mock view is defined in the same file that contains the unit tests for the presenter.

  2. Implement the members defined in the interface and provide additional elements to allow you test the presenter interaction with the view. For example, you can add public properties or fields to your mock view to test whether the presenter actually sets a property value in the view.

    For example, the following code extracted from the Model-View-Presenter QuickStart shows a mock implementation for the ContactDetailView view. Note that the mock class implements the same interface as the real view (IContactDetailView).

    class MockContactDetailView : IContactDetailView
    {
        public bool SetViewModeCalled;
        public bool SetViewModeReadOnly;
        public bool SetViewControlsVisibleMode;
        public bool SetViewControlsVisibleCalled;
        public bool LoadStatesCalled;
        public ICollection<State> LoadStatesArgumentValue;
        public ContactDetailPresenter PresenterLoaded;
        public Customer CustomerLoaded;
    
        public event EventHandler EditClicked;
        public event EventHandler<DataEventArgs<Customer>> SaveClicked;
        public event EventHandler DiscardChangesClicked; 
        public event EventHandler UserControlLoaded;
    
        public void ShowCustomer(Customer customer)
        {
            CustomerLoaded = customer;
        }
    
        public void SetViewReadOnlyMode(bool readOnly)
        {
            SetViewModeCalled = true;
            SetViewModeReadOnly = readOnly;
        }
    
        public void SetViewControlsVisible(bool visible)
        {
            SetViewControlsVisibleCalled = true;
            SetViewControlsVisibleMode = visible;
        }
        
        public void LoadStates(ICollection<State> states)
        {
            LoadStatesCalled = true;
            LoadStatesArgumentValue = states;
        }
    
        public void FireEditClicked()
        {
            if (EditClicked != null)
            {
                EditClicked(this, EventArgs.Empty);
            }
        }
    
        public void FireSaveClicked(Customer customer) { // ... }
        public void FireDiscardChangesClicked() { // ... }
        public void FireUserControlLoaded() { // ... }
    
        public ContactDetailPresenter Presenter
        {
            get { return PresenterLoaded; }
        }
    }
    

    You can use the MockContactDetailView class to test the interaction of the presenter with the view. It implements methods that the presenter calls and sets public flags that you query in your unit tests. You use these flags to verify whether methods were invoked. The mock class also contains public fields (such as the LoadStatesArgumentValue field) that you can query in your unit tests code. You use these public fields to analyze the parameters passed to the methods invoked by the presenter. The mock view also includes methods that raise events (such as the FireEditClicked method). You invoke these methods to test how the presenter reacts to events raised by the real view.

The following procedure describes how to create mock classes for the model.

To create mock classes for the model

  1. If your presenter depends on model objects, such as services or controllers, create mock classes for these dependencies. To do this, create an interface for each dependency, and then implement the interface in your mock implementation (as you did for your mock view).
    Ff709846.note(en-us,PandP.10).gifNote:
    The How to: Create a Business Module recipe generates a mock module controller class you can use in your presenter's unit tests. The mock module controller is stored in the Mocks folder of the module's test project.

  2. If you must keep a reference to the concrete class in the presenter, you can create a mock class that extends the real class and overrides members. If you use this approach, you must have the real implementation of the dependency when you create the presenter.

    For example, the Model-View-Presenter QuickStart includes a mock class named MockCustomersDataSource (located in Modules\Contacts.Tests\Mocks\MockCustomersDataSource.cs) that is used to replace the real customer’s data source implementation when testing the presenter. This class implements the same interface that the data source implements (the ICustomersDataSource interface.)

    class MockCustomersDataSource : ICustomersDataSource
    {
        public IList<Customer> GetCustomersReturnValue = new List<Customer>();
        public bool GetCustomersCalled = false;
        public List<State> GetStatesReturnValue = new List<State>();
        public bool GetStatesCalled = false;
        public Customer UpdatedCustomerValue;
        public bool UpdateCustomerCalled = false;
    
        public IList<Customer> Customers
        {
            get
            {
                GetCustomersCalled = true;
                return GetCustomersReturnValue;
            }
    
        }
        public ICollection<State> States
        {
            get
            {
                GetStatesCalled = true;
                return GetStatesReturnValue;
            }
        }
    
        public void UpdateCustomer(Customer customer)
        {
            UpdateCustomerCalled = true;
            UpdatedCustomerValue = customer;
        }
    }
    

The following procedure describes how to implement the unit tests for the presenter.

To implement unit tests for the presenter

  1. Use the mock objects you created in the previous procedures to create your unit tests. For example, the following code extracted from the Model-View-Presenter QuickStart illustrates the kind of unit test code you can create. The OnViewLoadedLoadStates test method verifies that the ContactDetailPresenter class invokes the LoadStates method in the view when the OnViewInitialized method is invoked in the presenter and that it calls the GetStates method in the Customers data source.
    [TestClass]
    public class ContactDetailPresenterTestFixture
    {
        MockCustomersDataSource dataSource;
        MockContactsController controller;
        ContactDetailPresenter presenter;
        MockContactDetailView view;
    
        public ContactDetailPresenterTestFixture()
        {
        }
    
        [TestInitialize()]
        public void MyTestInitialize()
        {
            dataSource = new MockCustomersDataSource();
            controller = new MockContactsController();
    
            view = new MockContactDetailView();
            presenter = new ContactDetailPresenter(view, dataSource);
            presenter.Controller = controller;
        }
    
        [TestMethod]
        public void OnViewLoadedLoadStates()
        {
            dataSource.GetStatesReturnValue.Add(new State("WA","Washington"));
    
            presenter.OnViewInitialized();
    
            Assert.IsTrue(dataSource.GetStatesCalled);
            Assert.IsTrue(view.LoadStatesCalled);
            Assert.IsTrue(view.LoadStatesArgumentValue.Count > 0);
        }
    
        // ...
    }
    

    In the OnViewLoadedLoadStates test method, a mock data source and a mock view are used. Both mock objects are initialized in the MyTestInitialize method. In the first line of the OnViewLoadedLoadStates method, the mock data source is set up with a State object; the mock data source will return this object when the GetStates method is invoked. After that, the OnViewInitialized method is invoked on the presenter and three assertions are performed to verify the interaction of the presenter with the view and the data source:

    • Assert.IsTrue(dataSource.GetStatesCalled). This assertion verifies that the presenter called the GetStates method in the data source.
    • Assert.IsTrue(view.LoadStatesCalled). This assertion verifies that the presenter invoked the LoadStates method on the view.
    • Assert.IsTrue(view.LoadStatesArgumentValue.Count > 0). This assertion verifies that the presenter passed a non-empty collection of state objects to the LoadStates method.

For more code examples, see the Model-View-Presenter QuickStart.

Outcome

You will have the following elements in your solution:

  • A mock view
  • Mock classes for dependencies of the presenter
  • Unit tests for the presenter
Show:
© 2015 Microsoft