2017-01-18 19 views
0

私は単純なtablecontrollerを書くつもりです。私のバックエンドの単体テスト?TableController単体テスト

私はそれを達成できましたが、私が達成したのはApiControllersの単体テストを書くことですが、TableControllerの単体テストを書く方法はありますか?私がやりたいよ何

はこれです:

public class AuctionController : TableController<Auction> 
{ 
    protected override void Initialize(HttpControllerContext controllerContext) 
    { 
     base.Initialize(controllerContext); 
     MobileServiceContext context = new MobileServiceContext(); 
     DomainManager = new EntityDomainManager<Auction>(context, Request); 
    } 

    // GET tables/Auction 
    public IQueryable<Auction> GetAllAuction() 
    { 
     return Query(); 
    } 

    // GET tables/Auction/48D68C86-6EA6-4C25-AA33-223FC9A27959 
    public SingleResult<Auction> GetAuction(string id) 
    { 
     return Lookup(id); 
    } 

    // PATCH tables/Auction/48D68C86-6EA6-4C25-AA33-223FC9A27959 
    public Task<Auction> PatchAuction(string id, Delta<Auction> patch) 
    { 
     return UpdateAsync(id, patch); 
    } 

    // POST tables/Auction 
    public async Task<IHttpActionResult> PostAuction(Auction item) 
    { 
     Auction current = await InsertAsync(item); 
     return CreatedAtRoute("Tables", new { id = current.Id }, current); 
    } 

    // DELETE tables/Auction/48D68C86-6EA6-4C25-AA33-223FC9A27959 
    public Task DeleteAuction(string id) 
    { 
     return DeleteAsync(id); 
    } 
} 

と私はこのようなテストコントローラーをしたい:

私は多分にこれを取得することができるかを
[TestClass] 
public class AuctionControllerTests 
{ 
    private readonly AuctionController _controller; 

    public AuctionControllerTests() 
    { 
     _controller = new AuctionController(); 
    } 

    [TestMethod] 
    public void Fetch_all_existing_items() 
    { 
     Assert.Equal(2, _controller.GetAllTodoItems().ToList().Count); 
    } 
} 

作業???私はあなたの助けをたくさんいただきたいと思います。

答えて

1

今、私は偽物と本物にこだわるよ、私は後でフレームワークをあざける適用します、モックソリューションのおかげで、それが働いていたが、私はモックフレームワークを使用せずに、一般的な、より良い解決策を書いたので統合のためのdbs n回のテスト。

しかし、複数のEntityDataとDbContextを複数のコンテキストに適用するためにGeneric TableControllerを作成しました。また、インターフェイスの抽象化によってFakeContextを適用することもできましたが、この例には適用されません。

まずこれが私のBaseControllerです:

//This is an abstract class so we can apply inheritance to scalfolded tablecontrollers<T>. 
public abstract class BaseController<TModel, TDbContext> : TableController<TModel> where TModel : class, ITableData 
                         where TDbContext:DbContext, new() 
{ 
    protected override void Initialize(HttpControllerContext controllerContext) 
    { 
     base.Initialize(controllerContext); 
     var context = new TDbContext(); 
     SetDomainManager(new EntityDomainManager<TModel>(context, Request)); 
    } 

    public void SetDomainManager(EntityDomainManager<TModel> domainManager) 
    { 
     DomainManager = domainManager; 
    } 
} 

これは私のbasecontrollerと私のscalfoldedコントローラが適用さです!私の一般的なアプリケーションでは

public class AuctionController : BaseController<Auction, MobileServiceContext>  
{   
    public IQueryable<Auction> GetAllAuction() 
    { 
     return Query(); 
    } 

    // GET tables/Auction/48D68C86-6EA6-4C25-AA33-223FC9A27959 
    public SingleResult<Auction> GetAuction(string id) 
    { 
     return Lookup(id); 
    } 

    // PATCH tables/Auction/48D68C86-6EA6-4C25-AA33-223FC9A27959 
    public Task<Auction> PatchAuction(string id, Delta<Auction> patch) 
    { 
     return UpdateAsync(id, patch); 
    } 

    // POST tables/Auction 
    public async Task<IHttpActionResult> PostAuction(Auction item) 
    { 
     Auction current = await InsertAsync(item); 
     return CreatedAtRoute("Tables", new { id = current.Id }, current); 
    } 

    // DELETE tables/Auction/48D68C86-6EA6-4C25-AA33-223FC9A27959 
    public Task DeleteAuction(string id) 
    { 
     return DeleteAsync(id); 
    } 
} 

私はDbContextにあなたもSqlConnectionのか、私はこの例で使用したものであるなどのAzureなどのクラウド接続を避けるためにFakeDbContextsを適用できるような方法を適用することができます。

すべてこの2つのライブラリ私のバックエンドのプロジェクトであり、今私はユニットテストするために、あなたに私のテストプロジェクトを紹介しますTableController

public abstract class ControllerTestBase<TController, TModel, TDbContext> where TController : BaseController<TModel, TDbContext>, new() 
                      where TModel : class, ITableData 
                      where TDbContext: DbContext, new() 
{ 
    protected readonly TController Controller; 

    protected ControllerTestBase() 
    { 
     Controller = new TController(); 
     var context = new TDbContext(); 
     Controller.SetDomainManager(new EntityDomainManager<TModel>(context, new HttpRequestMessage())); 
    } 
} 

この抽象クラスには、[OK]ありがとうございますから、初期設定をSUPRESSすることができますテストライブラリはテストを実行するたびに汎用テストコンストラクタを呼び出し、必要なすべてのrequierementsを設定し、ApiControllerとして初期化するのが非常に直感的でないため、ArgumentNullExceptionsやInvalidOperationExceptionsなどのユニットテストtablecontrollerの一般的な問題を回避します。

あなたがこれを変更した場合最後に、あなたはこのようにテストを実行することができます:あなたが従うならば、あなたは今もあなたのTableControllersに適用される例として、このコードを使用することができ、私の一般的なアプリケーションに

[TestClass] 
public class AuctionControllerTest : ControllerTestBase<AuctionController, Auction, MobileServiceContext> 
{ 
    [TestMethod] 
    public void Fetch_All_Existing_Items() 
    { 
     Assert.AreEqual(1, Controller.GetAllAuction().ToList().Count); 
    } 
} 

おかげでインターフェイス分離の原則あなたはあなたのコントローラにFakeDbContextを適用することができます。

私のお手伝いをしてくださった方々に、このソリューションをご利用いただき誠にありがとうございます。

+0

編集提案の男に感謝!! –

2

はい。それは可能ですが、コードは単体テスト可能ではありません。ここで

  1. あなたのための手順は、次のコードに示すのようにコンテキストや要望などを設定する必要があり、あなたのdepedenciesを注入MobileServiceContextとDomainManager
  2. 方法を見つけています。

が、このコードから

public class ControllerUnitTestBase<T> where T: Controller  
{ 
    private Action<RouteCollection> _routeRegistrar; 
    private Mock<HttpRequestBase> _mockRequest; 

    protected virtual Action<RouteCollection> RouteRegistrar 
    { 
     get { return _routeRegistrar ?? DefaultRouteRegistrar; } 
     set { _routeRegistrar = value; } 
    } 

    protected Mock<HttpRequestBase> MockRequest 
    { 
     get 
     { 
      if (_mockRequest == null) 
      { 
       _mockRequest = new Mock<HttpRequestBase>(); 
      } 

      return _mockRequest; 
     } 
    } 

    public abstract T TargetController { get; } 

    protected void TargetSetup() 
    { 
     var routes = new RouteCollection(); 
     RouteRegistrar(routes); 

     var responseMock = new Mock<HttpResponseBase>(); 
     responseMock.Setup(x => x.ApplyAppPathModifier(It.IsAny<string>())).Returns((string url) => url); 

     var contextMock = new Mock<HttpContextBase>(); 
     contextMock.SetupGet(x => x.Request).Returns(MockRequest.Object); 
     contextMock.SetupGet(x => x.Response).Returns(responseMock.Object); 
     contextMock.SetupGet(x => x.Session).Returns(Mock<HttpSessionStateBase>().Object); 

     TargetController.ControllerContext = new ControllerContext(contextMock.Object, new RouteData(), TargetController); 
     TargetController.Url = new UrlHelper(new RequestContext(contextMock.Object, new RouteData()), routes); 
    } 

    protected void DefaultRouteRegistrar(RouteCollection routes) 
    { 
     routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 

     routes.MapRoute(
      name: "Default", 
      url: "{controller}/{action}/{id}", 
      defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }); 
    } 
} 

継承し、多分テストの初期化(セットアップ)でのテスト実行(前TargetSetup()を呼び出すことを確認してください(コードを使用すると、部品番号を使用していると仮定)。そして、あなたは、に優れています以下のように行く:

[TestClass] 
    public class AuctionControllerTests: TestControllerBase<AuctionController> 
    { 

    public AuctionController TargetController { 
     get {return new AuctionController();//inject your mocked dependencies}} 

    [TestInitialize] 
    public void SetUp() 
    { 
     TargetSetup() 
    } 
    } 
+0

申し訳ありませんが、あなたのコードにエラーがあります。正直なところ、それらを解決する方法はわかりません。 「UrlHelper」2つの引数を取るコンストラクタが含まれていない、 名「target」は、現在のコンテキストで 名前が存在しない「UrlParameter」私はそのために探していたものを現在のコンテキスト には存在しません。統合テストを行うには、私は結果を模擬してほしくない! –

+0

うん。私は目標を修正した。しかし、UrlHelperとUrlParameterはSystem.Webを使用して名前空間を追加します。 using System.Web.Mvc; using System.Web.Routing;テストプロジェクトでmvc/webアセンブリを参照する必要があります –

+0

ありがとうございました!!!それは働きました。また、私はテーブルコントローラーやapicontrollerのunderhoodを理解することができたので、私自身のジェネリックバージョンを開発する方法のアイデアをくれました。 –