2017-09-14 23 views
1

私はsitecoreの開発者です。 "EmailArticleController"コントローラのロジックをテストするためのサンプルSitecoreヘリックスユニットテストプロジェクトを作成したいインデックス()アクションメソッド:Sitecore.Mvc.Presentation.RenderingContextを使用してGlassControllerアクションをテストする方法

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これは私が私のユニットテストクラスに持っているものです。

[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); 
    } 
} 

は基本的に私はそれが(文字列)を持っていることを確認し、レンダリングコンテキストをモックとしたいデータソース値は "/ sitecore/home/..."のような値に設定され、私はそれをコントローラのコンストラクタに送信して(適切な方法であれば)、Index(int)メソッドを呼び出すと同時に、_businessLogic(この場合のインタフェースのみ)を確認します?)は、ビューを返す前に同じ値にdataSourceを設定しています。

これを実行するための正確なコードは何ですか?ありがとう!

答えて

1

コードをRenderingContext.Current.Rendering.DataSourceのような静的な依存関係に強く結合すると、コードを孤立してテストするのが難しくなります。

RenderingContextへの静的アクセスをカプセル化するためのラッパーを作成することをお勧めします。あなたは、あなたが今するすべての依存関係を模擬することができます明示的にコンストラクタ・インジェクション

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); 
    } 
} 

を経由してその抽象化に依存するように、あなたのコントローラーを更新してしまうGitHubの

public interface IRenderingContext { 
    string GetDataSource(); 
} 

//... 

using Sitecore.Mvc.Presentation; 

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

Glass.Mapperリポジトリで見つけたコード例を参照すると、コントローラを単独でテストすることができます。

[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); 

     var datasourceId = "fake_datasourceId"; 
     var renderingContext = Mock.Of<IRenderingContext>(_ => _.GetDataSource() == datasourceId); 

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

     //Act 
     var result = controllerUnderTest.Index() as ViewResult; 

     //Assert 
     Assert.IsNotNull(result); 
     businessLogicFake.Verify(_ => _.SetDataSourceID(datasourceId), Times.AtLeastOnce()); 
    } 
} 

本番コードは明らかに依存関係の実行時の解像度のためのあなたのDIコンテナと抽象化と実装を登録します。

+0

ありがとう、あなたの答えは大変です!それは魅力のように働いた!!あなたは、「RenderingContext.Current.Rendering.DataSourceのような静的な依存関係にコードを密接に結合すると、コードを孤立してテストするのが難しくなる可能性があります。 ユニットテストのためにEmailArticleController()クラスにコードを絶対に追加する必要がある他の理由はありますか?あなたは、コンストラクタに何も送ることなく元のIndex()メソッドを単体テストできる方法がないことを暗示していますか? – user3034243

+1

@ user3034243それはもっと設計の問題です。その静的依存関係を制御することはできません。つまり、通常の操作の外で必要になったときにどのように初期化されるかを制御できません。それはあなたが所有していないコードを制御できないため、孤立してテストすることが困難です。 SOLIDのようなトピックを読むと、メンテナンスしやすいクリーンなコードを設計することと、テストを含む方法をどのように関係させるのかを理解することができます。 – Nkosi

+0

@ user3034243もう一つの可能​​な方法があるかもしれないと確信していますが、簡単にそれを正しく設計することができたら、それを捜すことができます。 – Nkosi

関連する問題