2009-09-17 10 views
6

He implementado mi controlador base de mvc llamado DefaultController usando el patrón de inyección de dependencia para poder construir casos de prueba. Ejemplo a continuación:¿Cómo resolver la inyección de dependencia del controlador base para fines de prueba?

public class DefaultController : Controller 
{ 
    protected readonly ISessionHelper _sessionHelper; 
    string _thisUserOpenID; 
    protected IUsersRepository _UserRepository; 
... 
    public DefaultController() 
    { } //not for testing 

    public DefaultController(ISessionHelper session, IUserRepository repo) 
    { 
     _sessionHelper=session; 
     _UserRepository = repo; 
    } 
} 

Entonces tengo mis controladores que utilizan este controlador, HomeController, UserController, etc.

Ahora, la construcción de algunos casos de prueba, me encontré en una situación en la que no sé cómo en realidad usa el patrón de dependencia de inyección.

[TestMethod] 
    public void Welcome_Message_In_ViewData_Has_Coockie_User_Display_Name() 
    { 
     // Below I want to insert FakeRepositories using 
     //ISessionHelper and so on. but the constructor 
     //for homecontroller don't have it. 
     HomeController controller = new HomeController(); 

¿Alguna idea?

Respuesta

5

Su HomeController necesita tener un constructor "inyectable" coincidente, que luego llamaría al constructor base.

public HomeController(ISessionHelper session, IUserRepository repo) 
    : base(session, repo) 
    { 

    } 

Ahora, en su prueba, debe crear su HomeController usando ese constructor, y pasar en un objeto de burla por sesión y repositorio de usuarios. Hablando de burlas, también podría interesarte Scott Hanselman's MvcMockHelpers classes, con código para muchos frameworks burlones populares.

3

No veo por qué tienes dos constructores. Solo deberías tener uno, deshacerte del constructor sin parámetros. Utilizando un marco DI como Castle Windsor, o mi preferido, Autofac se encargará de todo esto por ti. Luego, en lo que respecta a las pruebas, utiliza algo como Moq. Es decir

public DefaultController(ISessionHelper session, IUserRepository repo) 
{ 
    _sessionHelper = session; 
    _UserRepository = repo; 
} 

Registro defaultController, ISessionHelper y IUserRepository con su marco DI. Algo a lo largo de las líneas de:

Register(new DefaultController()); (it is something like that in Autofac) 
Register<SessionHelper>().As<ISessionHelper>(); 
Register<UserRepository>().As<IUserRepository>(); 

esta manera, se puede tirar defaultController del recipiente y el marco DI inyectará los dos parámetros para usted. Envuelvo un método estático para acceder a mi contenedor de DI, que parece:

var controller = IoC.Resolve<DefaultController>(); 

Básicamente cabeza a Autofac y echar un vistazo. También hay un módulo web para registrar sus Controladores por usted.

Luego, para probar simplemente use Moq, o encuentre alguna forma de "AutoMocker" (google it). Haría:

var session = new Mock<ISessionHelper>(); 
var repo = new Mock<IUserRepository>(); 
repo.Setup(s => s.FindById(123)).Returns(new User()); 

var conroller = new DefaultController(session.Object, repo.Object); 
controller.Execute(); 

También ewww repositories. Con .Net y genéricos, etc. solo cree una buena ISession.

var session = IoC.Resolve<ISession>(); 
var user1 = session.Get<User>(123); 
var user2 = session.Get<User>(u => u.Username == "admin"); 
session.Update(user3); 

Significa que solo necesita pasar una cosa y puede usarla para lo que sea. En lugar de tener que pasar, a veces hay muchos repositorios. También te prepara muy bien para el patrón de unidad de trabajo.

+0

Solo para tener en cuenta: si se deshace de los constructores sin parámetros, entonces también necesita desplegar sus propias fábricas de Controladores. – womp

+0

¿Por qué las fábricas? En el caso anterior, el marco DI inyecta todo por ti. Cosas como ISessionHelper y IUserRepository deberían ejecutarse en una sesión estática, y lo más probable es que no tengan ningún valor que desee pasar. Todo lo que tiene que hacer es llamar a var controller = IoC.Resolve () ; y está todo construido para ti. Al inyectar a través del constructor, significa que debe pasar las variables a través de los parámetros del método o establecer las propiedades públicas (muy parecido al patrón de comando). – Bealer

Cuestiones relacionadas