Trying to understand unit test with the service locator pattern

Apr 27, 2011 at 3:20 PM

Hi.

I already succesfully implemented the service locator pattern in a webpart project, in my simple example I have to return the number of rows of a list with a given query.  

I followed all code on the CHM file and understood it, tested and it works as expected.

 

However, (please dont laugh at me), I am not an expert on unit testing.

 

My interface:

 

public interface IListsHelper
{
        int GetListRowCount(string listName, string url, string query);
}

 

My Implementation class.

public int GetListRowCount(string listName, string url, string query)
        {
            using(SPSite site = new SPSite(url))
            {
                using (SPWeb oWebsiteRoot = site.RootWeb)
                {
                    SPList oList = oWebsiteRoot.Lists[listName];
                    SPQuery oQuery = new SPQuery();
                    oQuery.Query = query;
                    SPListItemCollection collListItems = oList.GetItems(oQuery);
                    return collListItems.Count;
                }
            }
        }

The code on the webpart that uses the service locator pattern.

  private void GetRowCount()
        {   
            IServiceLocator serviceLocator = SharePointServiceLocator.GetCurrent();

            IListsHelper serviceList = serviceLocator.GetInstance<IListsHelper>();

            string query = "<Where><Eq><FieldRef Name='Vendedor'/>" +
                    "<Value Type='Text'>Luis</Value></Eq></Where>";

            int x = serviceList.GetListRowCount("Sales Marketing", SPContext.Current.Site.Url, query);

        }

This worked perfectly, then I tried to do the mock class.  Here is where you shouldnt laugh

1.  What logic should I put inside the method of the mock? I always put return 1, but I have no idea exactly what to do there.

public class MockLists : IListsHelper
    {
        public int GetListRowCount(string listName, string url, string query)
        {
            return 1;
        }
    }

2.  I created a unit test project with the following code, I debugged it, and instead of using the mock method is uses the real method.

  [TestMethod]
        public void TestRowCount()
        {
            // In this section we set up the SharePoint Service Locator. We replace the default current service locator with a new instance 
            // of the ActivatingServiceLocator class. We register our mock classes as the default implementations of ILists.
            // Finally we use the service locator to instantiate our mock classes
            ActivatingServiceLocator replaceLocator = new ActivatingServiceLocator();
            SharePointServiceLocator.ReplaceCurrentServiceLocator(replaceLocator);
            replaceLocator.RegisterTypeMapping<IListsHelper, MockLists>(InstantiationType.AsSingleton);

            list = SharePointServiceLocator.GetCurrent().GetInstance<IListsHelper>() as MockLists;            
            
            ListsHelper mockList = new ListsHelper();

            // In this section we perform the action that we want to test. We create a new instance of Lists 
            // and we call the GetListRowCount method. 
            string query = "";
            int result = mockList.GetListRowCount("Wraps", "SiteUrl", query);

            //In this section we use various assert statements to verify that the Lists class behaved as expected
            Assert.AreEqual(1,result);

            SharePointServiceLocator.Reset();
     
        }

To be honest I didnt understand much the explanation of the guidance team in this last part.

 

Coordinator
May 2, 2011 at 1:28 PM

We of course wouldn't laugh, some of these patterns are different than devs typically experience before starting with unit testing and take some getting used to.

This code looks correct with regards to the service locator part, but the usage isn't quite right, and you use a concrete instantiation ( ListsHelper mockList = new ListsHelper(); ) rather than using the service locator.  This area can be confusing until you get used to the pattern.

The idea of a mock in a unit test is to implement test code for a provider that produces known results into the consumer.  The consumer of the service gets a reference to the provider using service location.  In the example above, that means that if you wrote a unit test for the web part, then you would use your mock.  However it appears that what you are doing is testing the ListHelper class above.  That doesn't make much sense.  It doesn't work because you directly instantiate mockList from the concrete class ListsHelper.  This will definitely be the original implementing class, not the mock.  If instead in the code you use list, this will then call into your mock.

int result = mockList.GetListRowCount("Wraps", "SiteUrl", query);

replaced with

int result = list.GetListRowCount("Wraps", "SiteUrl", query);

But that really doesn't test anything.  A test would instead be against the method GetRowCount in the web part.  As far as I can tell this isn't doing much right now.  Often a pattern like Model-View-Presenter will be used with a web part to separate the logic and the ux components.  In this case, a mock is also developed for the view (a thin veneer for the user interface), and typically provided to the presenter when the web part is created (called injection).  the presenter then updates the view.  You can replace the view with a substitute as well to test your logic. 

You can read about the model-view-presenter at http://msdn.microsoft.com/en-us/library/ff649571.aspx.

In terms of testing in classes that use SharePoint logic, this can be tricky.  Moles and Stubs can be used for this.  This is explained here: http://msdn.microsoft.com/en-us/library/ff798466.aspx.

 

May 2, 2011 at 1:43 PM

thank you, I already read the mvp presenter and I think I understand it more, I will post in a few minutes a question, because the example on the guidance is for sandboxed solutions using a regular webpart, and I want to make the pattern work in a visual webpart.

Aug 30, 2011 at 9:24 AM

Hi.. There's nothing to laugh here about,The code is quite correct at it.

I am a little bit less concerned about it,also further looking for the accurate answer.

                  Thanks!

Coordinator
Aug 30, 2011 at 12:57 PM

Maybe I am missing something.  What production code do you think is being tested by the unit test as written?