2016-04-20 66 views
3

說做setter注入,我有以下控制器我如何傳遞一個模仿的對象,當我在ASP.NET MVC控制器

public class UsersController : Controller 
{ 
    private IUsersRepository UsersRepository { get; } 
    public UsersController() 
    { 
     UsersRepository = DependencyResolver.Current.GetService(typeof(IUsersRepository)) as IUsersRepository; 
    } 
    public ActionResult Index() 
    { 
     MyUserDefinedModel data = UsersRepository.MyRepository(); 
     return View(data); 
    } 
} 

現在我想嘲笑IUsersRepository並把它傳遞給控制器我的測試腳本。

下面我的測試代碼

public class UsersListTest 
    { 
     private UsersController usersController = new Mock<IUsersRepository>(); 
     private Mock<IUsersRepository> usersRepository = new UsersController(); 
     [TestMethod] 
     public void TestMethod1() 
     { 
      //usersRepository.Setup(x => x.Get()).Returns(users); 
     } 
    } 

至於因爲private IUsersRepository UsersRepository { get; }私人的,我不能夠通過的IUsersRepository模擬。

在這種情況下編寫單元測試和模擬會是個好主意。

+0

爲什麼你不註冊你的模擬對象到DI容器?它可能是最簡單的解決方案.... –

回答

2

您有測試故障的原因是因爲你Controller類使用Service Locator anti-pattern。服務定位器既可以是全局實例(DependencyResolver.Current),也可以是允許在運行時解決依賴關係的抽象。服務定位器的諸多缺點之一就是它在測試中帶來的問題。

您應該遠離Service Locator模式,並使用依賴注入來代替構建器注入。您的應用程序組件應該有single public constructor,那些構造函數應該只不過是storing the incoming dependencies。這將導致以下UsersController實現:

public class UsersController : Controller 
{ 
    private IUsersRepository usersRepository; 
    public UsersController(IUsersRepository usersRepository) 
    { 
     this.usersRepository = usersRepository; 
    } 
    public ActionResult Index() 
    { 
     return View(this.usersRepository.MyRepository()); 
    } 
} 

有了這個地方,單元測試變得簡單:

public class UsersControllerTests 
{ 
    [TestMethod] 
    public void Index_Always_CallsRepository() 
    { 
     // Arrange 
     var repository = new Mock<IUsersRepository>(); 
     var controller = CreateValidUsersController(repository.Instance); 

     // Act 
     var result = controller.Index(); 

     // Assert 
     Assert.IsTrue(repository.IsCalled); 
    } 

    // Factory method to simplify creation of the class under test with its dependencies 
    private UsersController CreateValidUsersController(params object[] deps) { 
     return new UsersController(
      deps.OfType<IUsersRepository>().SingleOrDefault() ?? Fake<IUsersRepository>() 
      // other dependencies here 
      ); 
    } 

    private static T Fake<T>() => (new Mock<T>()).Instance; 
} 

但這不過,迫使你改變MVC的默認IControllerFactory,因爲失MVC只能使用默認構造函數處理控制器。但是,這是微不足道的,看起來如下:

public sealed class CompositionRoot : DefaultControllerFactory 
{ 
    private static string connectionString = 
     ConfigurationManager.ConnectionStrings["app"].ConnectionString; 

    protected override IController GetControllerInstance(RequestContext _, Type type) { 

     if (type == typeof(UsersController)) 
      return new UsersController(new UsersRepository()); 

     // [other controllers here] 

     return base.GetControllerInstance(_, type); 
    } 
} 

您的新控制器工廠可以如下勾搭成MVC:

public class MvcApplication : System.Web.HttpApplication 
{ 
    protected void Application_Start() { 
     ControllerBuilder.Current.SetControllerFactory(new CompositionRoot()); 

     // the usual stuff here 
    } 
} 

你可以找到一個更完整的例子here

+0

同意,但大多數DI容器已經有一個(nuget)包與ASP.NET集成。無需自己編寫ControllerFactory。 –

+0

@HenkHolterman:我不認爲OP正在使用DI容器。 – Steven

+0

@Riki:我添加了一個單元測試示例。 – Steven

1

您可以添加一個構造函數,它允許您提供IUsersRepository的模擬。您的默認構造函數從DependencyResolver與實例調用此方法,像這樣:

public class UsersController : Controller 
{ 
    private IUsersRepository UsersRepository { get; } 

    public UsersController(IUsersRepository usersRepository) 
    { 
     UsersRepository = usersRepository; 
    } 

    public UsersController():this(DependencyResolver.Current.GetService(typeof(IUsersRepository)) as IUsersRepository) 
    { 

    } 

    public ActionResult Index() 
    { 
     MyUserDefinedModel data = UsersRepository.MyRepository(); 
     return View(data); 
    } 

}

相關問題