2016-04-04 9 views
0

私はそれを動作させることができますが、私はベストプラクティスが何であるか、なぜそうであるかを知りたいと思います。私はコントローラ、モデル、リポジトリを持っており、今はコントローラのユニットテストをしたいと思います。私はちょうど正しいビューが返されていることを確認するための簡単なテストを書いています。ユニットのコントローラのテスト - 接続文字列の処理方法

これは、コントローラの私の方法であって、ここで

public ActionResult Selections(SelectionsViewModel model) 
    { 
     for (int i = 0; i < model.Sends.Count; i++) 
     { 
      Send send = new Send(new SendService(new Database().GetConnectionString())) 
      { 
       SendID = model.Sends[i].SendID, 
       Title = model.Sends[i].Title, 
       Subject = model.Sends[i].Subject, 
       SentDate = model.Sends[i].SentDate, 
       TimesViewed = model.Sends[i].TimesViewed, 
       Include = model.Sends[i].Include, 
       Exclude = model.Sends[i].Exclude 
      }; 
      send.UpdateIncludeExclude(); 
     } 

     return View(model); 
    } 

は私のSendServiceのコンストラクタを介して送信されているDatabaseクラスの私のGetConnectionString()メソッドです。

public string GetConnectionString() 
    { 
     return System.Configuration.ConfigurationManager.ConnectionStrings["DEVConnectionString"].ToString(); 
    } 

そして最後に、私のユニットテスト:

[Test] 
    public void TestAssignmentSelections() 
    { 
     var obj = new AssignmentController(); 

     var actResult = obj.Selections() as ViewResult; 

     NUnit.Framework.Assert.That(actResult.ViewName, Is.EqualTo("Selections")); 
    } 

は今、私のユニットテストは失敗し、私はその理由を取得。私のユニットテストプロジェクトは、接続文字列がどこにあるかをテストしているプロジェクトのweb.configへのアクセス権がありません。

私はいくつかの調査を行いました。明らかに、単体テストプロジェクトにweb.configを追加し、そこに接続文字列を入れても動作するようになりますが、それはハックのようです。

これについてはどのような方法が最適ですか?これに対応するコードを書く別の方法がありますか?

答えて

3

コントローラユニットをテスト可能にしたいですか?これをしないでください。このコードでは

new SendService(

、あなたは具体的なサービスの実装&データアクセスコードの実装をハードコーディングされています。単体テストでは、実際にデータベースからデータにアクセスすべきではありません。代わりに、模擬データアクセスの実装を提供する必要があります。

ここでインターフェイスが来るので、SendServiceのインターフェイスを作成する必要があります。

public interface ISendService 
{ 
    void SomeMethod(); 
} 

今、あなたのSendServiceは、私たちがISendServiceの実装を注入しますコンストラクタを持っているあなたのコントローラーを更新今

public class SendService : ISendService 
{ 
    public void SomeMethod() 
    { 
    // Do something 
    } 
} 

このインタフェースの具体的な実装になります。

public class YourController : Controller 
{ 
    private ISendService sendService; 
    public YourController(ISendService sendService) 
    { 
    this.sendService = sendService; 
    }  
    public ActionResult YourActionMethod() 
    { 
    // use this.sendService.SomeMethod(); 
    } 
} 

そして、あなたは、コードの実行時インタフェースの実装は、使用するMVCフレームワークを伝えるために、いくつかの依存性注入フレームワークを使用することができます。 MVC6を使用している場合、それはあなたが使用できるinbuilt依存性注入プロバイダを持っています。したがって、Startupクラスに進み、ConfigureServicesメソッドでは、インターフェイスを具体的な実装にマップできます。あなたはMVCの以前のバージョンである場合

public class Startup 
{ 
    public void ConfigureServices(IServiceCollection services) 
    { 
    services.AddTransient<ISendService, SendService>(); 
    } 
} 

、あなたはあなたが後でデータアクセス/サービス層に同じアプローチを行うことができますユニティ、NinjectなどのようなDIフレームワークを考慮することができます。つまり:データアクセス用のインタフェースを作成し、それをSendServiceに挿入します。

public Interface IDataAccess 
{ 
    string GetName(int id); 
} 

および特定のデータアクセスコードを使用して実装/ ORM

public class EFDataAccess : IDataAccess 
{ 
    public string GetName(int id) 
    { 
    // return a string from db using EF 
    } 
} 

だから今、あなたのサービスクラスを使用すると、モックを作成することができ、あなたのユニットテストで

public class SendService : ISendService 
{ 
    private IDataAccess dataAccess; 
    public SendService(IDataAccess dataAccess) 
    { 
    this.dataAccess=dataAccess; 
    } 
    // to do : Implement methods of your ISendService interface. 
    // you may use this.dataAccess in those methods as needed. 
} 

になりますデータベースにアクセスする代わりに静的データを返すインターフェイスの実装。

たとえば、Moqモッキングフレームワークを使用している場合は、これを行うことができます。

var m = new Mock<IDataAccess>(); 
var m.Setup(s=>s.GetName(It.IsAny<int>())).Returns("Test"); 
var s = new SendService(m); 
var result= s.SomeMethod(); 
+0

これは参考になりましたが、変更する必要があると思います。ありがとうございました。 – halterdev

関連する問題