2017-09-14 3 views
1

Je suis développeur de sitecore et je souhaite créer un exemple de projet de test d'unité d'hélice sitecore pour tester la logique que vous voyez dans notre contrôleur "EmailArticleController" méthode d'action Index():Comment tester une unité Action GlassController qui utilise Sitecore.Mvc.Presentation.RenderingContext

using Sitecore.Mvc.Presentation; 

public class EmailArticleController : GlassController 
{ 
    //logic in below Index() method is what I want to test 
    public override ActionResult Index() 
    { 
     var _emailArticleBusiness = new EmailArticleBusiness(); 
     var model = _emailArticleBusiness.FetchPopulatedModel; 
     var datasourceId = RenderingContext.Current.Rendering.DataSource; 
     _emailArticleBusiness.SetDataSourceID(datasourceId); 

     return View("~/Views/EmailCampaign/EmailArticle.cshtml", model); 
    } 

    //below is alternative code I wrote for mocking and unit testing the logic in above Index() function 
    private readonly IEmailArticleBusiness _businessLogic; 
    private readonly RenderingContext _renderingContext; 

    public EmailArticleController(IEmailArticleBusiness businessLogic, RenderingContext renderingContext) 
    { 
     _businessLogic = businessLogic; 
     _renderingContext = renderingContext; 
    } 

    public ActionResult Index(int forUnitTesting) 
    { 
     var model = _businessLogic.FetchPopulatedModel; 
     // *** do below two lines of logic somehow go into my Unit Testing class? How? 
     var datasourceId = _renderingContext.Rendering.DataSource; 
     _businessLogic.SetDataSourceID(datasourceId); 
     // *** 
     return View("~/Views/EmailCampaign/EmailArticle.cshtml", model); 
    } 
} 

OK c'est ce que j'ai dans ma classe de test unitaire:

[TestClass] 
public class UnitTest1 
{ 
    [TestMethod] 
    public void Test_EmailArticleController_With_RenderingContext() 
    { 
     //Arrange 
     var businessLogicFake = new Mock<IEmailArticleBusiness>(); 

     var model = new EmailArticleViewModel() 
     { 
      ArticleControl = new Article_Control() { }, 
      Metadata = new Metadata() { } 
     }; 

     businessLogicFake.Setup(x => x.FetchPopulatedModel).Returns(model); 

     // I'm not sure about the next 3 lines, how do I mock the RenderingContext and send it into the constructor, given that it needs a DataSource value too? 
     var renderingContext = Mock.Of<Sitecore.Mvc.Presentation.RenderingContext>(/*what goes here, if anything?*/) { /*what goes here, if anything?*/ }; 

     EmailArticleController controllerUnderTest = new EmailArticleController(businessLogicFake.Object, renderingContext); 

     var result = controllerUnderTest.Index(3) as ViewResult; 

     Assert.IsNotNull(result); 
    } 
} 

Fondamentalement, je veux moquer un contexte de rendu, assurez-vous qu'il a une (chaîne) DataSource valeur définie à une valeur telle que "/ sitecore/home/...", je w ant pour l'envoyer dans le constructeur du contrôleur (si c'est la bonne méthode), appelez la méthode Index (int), et en même temps assurez-vous mon _businessLogic, qui est seulement une interface dans ce cas (devrait-elle être la classe concrète ?) a sa source de données définie à la même valeur avant de renvoyer la vue.

Quel est le code exact pour faire tout cela? Merci!

Répondre

1

Le couplage étroit de votre code à des dépendances statiques telles que RenderingContext.Current.Rendering.DataSource peut rendre le test de votre code difficile.

Je vous suggère de créer un wrapper pour encapsuler l'accès statique au RenderingContext. Se référant à des exemples de code trouvés au référentiel Glass.Mapper sur GitHub

public interface IRenderingContext { 
    string GetDataSource(); 
} 

//... 

using Sitecore.Mvc.Presentation; 

public class RenderingContextWrapper : IRenderingContext { 
    public string GetDataSource(){ 
     return RenderingContext.CurrentOrNull.Rendering.DataSource; 
    } 
} 

Vous pouvez ensuite mettre à jour votre contrôleur dépend explicitement que l'abstraction par injection constructeur

public class EmailArticleController : GlassController { 
    private readonly IEmailArticleBusiness businessLogic; 
    private readonly IRenderingContext renderingContext; 

    public EmailArticleController(IEmailArticleBusiness businessLogic, IRenderingContext renderingContext) { 
     this.businessLogic = businessLogic; 
     this.renderingContext = renderingContext; 
    } 

    public ActionResult Index() { 
     var model = businessLogic.FetchPopulatedModel; 
     var datasourceId = renderingContext.GetDataSource(); 
     businessLogic.SetDataSourceID(datasourceId); 
     return View("~/Views/EmailCampaign/EmailArticle.cshtml", model); 
    } 
} 

Vous êtes maintenant en mesure de se moquer de toutes les dépendances d'être capable de tester le contrôleur isolément. Votre code de production enregistrerait évidemment l'abstraction et l'implémentation avec votre conteneur DI pour la résolution d'exécution des dépendances.

+0

Merci beaucoup, beaucoup pour votre réponse !! Ça a marché comme sur des roulettes!! Vous avez déclaré que "Le couplage étroit du code à des dépendances statiques telles que RenderingContext.Current.Rendering.DataSource peut compliquer la tâche de test du code.Il est préférable de créer un wrapper pour encapsuler l'accès statique au RenderingContext." Existe-t-il d'autres raisons pour lesquelles nous devons absolument ajouter du code à la classe EmailArticleController() afin de le tester unitaire? Vous laissez entendre que nous ne pouvons pas tester la méthode Index() originale sans envoyer quoi que ce soit dans un constructeur, n'est-ce pas? – user3034243

+1

@ user3034243 c'est plus une question de design. Vous n'avez aucun contrôle sur cette dépendance statique, ce qui signifie que vous n'avez aucun contrôle sur la manière dont elle est initialisée en cas de besoin en dehors de ses opérations normales. C'est ce manque de contrôle sur le code que vous ne possédez pas qui le rend difficile à tester dans l'isolement. Lisez sur le sujet comme SOLID et vous obtiendrez une meilleure compréhension de la façon dont il se rapporte à la conception de code propre qui est facile à maintenir, qui comprendrait également des tests. – Nkosi

+0

@ user3034243 Je suis presque sûr qu'il y a peut-être un autre moyen de le faire, mais pour le chasser quand on peut facilement le concevoir correctement pour commencer, je choisirais la conception du nettoyeur à chaque fois. – Nkosi