Der Grund, dass Sie Probleme mit dem Testen haben, weil Ihr Controller
Klasse die Service Locator anti-pattern verwendet. Ein Service Locator ist entweder eine globale Instanz (DependencyResolver.Current
) oder eine Abstraktion, die es ermöglicht, Abhängigkeiten zur Laufzeit aufzulösen. Einer der vielen Nachteile des Service Locators sind die Probleme, die beim Testen auftreten.
Sie sollten sich vom Service Locator-Muster entfernen und stattdessen die Abhängigkeitsinjektion verwenden, was eine vorteilhafte Konstruktorinjektion darstellt. Ihre Anwendungskomponenten sollten eine single public constructor haben und diese Konstruktoren sollten nichts weiter tun als storing the incoming dependencies. Dies wird in der folgenden UsersController
Implementierung führen:
public class UsersController : Controller
{
private IUsersRepository usersRepository;
public UsersController(IUsersRepository usersRepository)
{
this.usersRepository = usersRepository;
}
public ActionResult Index()
{
return View(this.usersRepository.MyRepository());
}
}
In diesem Ort, Testeinheit wurde trivial:
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;
}
Dies ist jedoch nicht, Sie zwingen MVC-Standard IControllerFactory zu ändern, da out-of- In der Box kann MVC nur Controller mit einem Standardkonstruktor behandeln. Aber das ist trivial und sieht wie folgt aus:
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);
}
}
Ihre neue Controller Fabrik kann in MVC angeschlossen werden, wie folgt:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start() {
ControllerBuilder.Current.SetControllerFactory(new CompositionRoot());
// the usual stuff here
}
}
Sie können here ein vollständigeres Beispiel finden.
Warum registrieren Sie Ihr Mock-Objekt nicht im DI-Container? es könnte die einfachste Lösung sein .... –