unit-testingmodel-view-controllermockingsitecore-mvcsitecore9

How to Unit Test a Method in Sitecore MVC having tightly coupled Dependency on two distinct Sitecore Contexts?


I have to unfortunately write Unit Tests for a legacy Sitecore MVC code base where two distinct Sitecore Contexts are called. I understand this comes under Integration Testing but i don't have the option of educating my project Leads on that front. So i have chosen to use FakeDb for emulating Sitecore Instance and NSubstitute for substituting injected Dependencies (can't use any Profilier API Frameworks like MS Fakes, TypeMock etc because of Budget constraints). I am providing the code below:

Method to be UnitTested

 public bool DubiousMethod()
 {
    // This HttpContext call is pain area 1. This gets resolved when i call it using ItemContextSwitcher in Unit Tests.
    string currentUrl = HttpContext.Current.Request.RawUrl; 

   // This Sitecore Context call to Site Name is pain area 2. This gets resolved when Unit Tests are run under SiteContextSwitcher.
    string siteName = Sitecore.Context.Site.Name;

    return true/False;
 }

Unit Test Method

[Fact]

public void DubiousMethodUT()
{
 // create a fake site context            
        var fakeSite = new Sitecore.FakeDb.Sites.FakeSiteContext(
          new Sitecore.Collections.StringDictionary
           {
              { "name", "website" }, { "database", "web" }, { "rootPath", "/sitecore/content/home" },
              { "contentStartItem", "home"}, {"hostName","https://www.myorignalsiteurl.com"}                 

           });

 using (new Sitecore.Sites.SiteContextSwitcher(fakeSite))
        {                           
            //DubiousClassObject.DubiousMethod(home) // When Debugging after uncommenting this line i get correct value in **Sitecore.Context.Site.Name**
            using (Sitecore.FakeDb.Db db = new Sitecore.FakeDb.Db
                 {

                   new Sitecore.FakeDb.DbItem("home") { { "Title", "Welcome!" } ,

                   new Sitecore.FakeDb.DbItem("blogs") }
                  })

            {

                Sitecore.Data.Items.Item home = db.GetItem("/sitecore/content/home");
                //bool abc = confBlogUT.IsBlogItem(home);
                using (new ContextItemSwitcher(home))
                {
                    string siteName = Sitecore.Context.Site.Name;
                    var urlOptions = new Sitecore.Links.UrlOptions();
                    urlOptions.AlwaysIncludeServerUrl = true;
                    var pageUrl = Sitecore.Links.LinkManager.GetItemUrl(Sitecore.Context.Item, urlOptions);
                    HttpContext.Current = new HttpContext(new HttpRequest("", pageUrl.Substring(3), ""), new HttpResponse(new StringWriter()));



                    Assert.False(DubiousClassObject.DubiousMethod(home); //When Debugging after commenting above DubiousMethodCall i get correct value for **HttpContext.Current.Request.RawUrl**
                }
            }
        }
    }

As you can observe that when i try to call the method from FakSiteContext then i am getting the correct value for Sitecore.Context.Site.Name however my code breaks when HttpContext.Current.Request.RawUrl is invoked in the method. Opposite happens when i invoke the method from ContextItemSwitcher(FakeItem) context. So far i have not been able to find a way to merge both the Contexts (which i believe is impossible in Sitecore). Can anyone suggest if i run my Unit Tests in an overarching context where i am able to contrl fakeSite Variables as well as FakeItem context variables as well and by extensions any other Sitecore Context calls?

Any help would be appreciated.


Solution

  • I'd recommend to take a look at Unit testing in Sitecore article as it seem to be what you need.

    In short - you'll need to do a few adjustments in your code to make it testable:

    1) Replace static HttpContext with abstract HttpContextBase (impl. HttpContextWrapper) so that everything can be arranged - DubiousMethod gets an overload that accepts DubiousMethod(HttpContextBase httpContext).

    2) As for Sitecore Context data - it has Sitecore.Caching.ItemsContext-bound semantics (as mentioned in the article), so you could cleanup the collection before/after each test to get a sort of isolation between tests.

    Alternatively you could bake a similar wrapper for Sitecore.Context as ASP.NET team had done for HttpContext -> HttpContextBase & impl HttpContextWrapper.