asp.net-mvc unit-testing dependency-injection moq

asp.net mvc - Cómo simular/Stub o simplemente ignorar HttpRequest cuando prueba la unidad



asp.net-mvc unit-testing (2)

Haga de DeviceDetection una dependencia concreta de DemoController :

public class DemoController : Controller { private readonly ICommonOperationsRepository _commonRepo; private readonly DeviceDetection dd; public DemoController ( ICommonOperationsRepository commonRepo, DeviceDetection dd) { _commonRepo = commonRepo; this.dd = dd; } public ActionResult Default() { var model = new DemoModel(); try { this.dd.DetectDevice(); model.ListTopListing.AddRange(_commonRepo.GetListings()); } catch (Exception ex) { ExceptionHandler objErr = new ExceptionHandler(ex, "DemoController .Default()/n Exception : " + ex.Message); objErr.LogException(); } return View(model); } }

Esto debería permitirle crear una instancia de DemoController sin depender de la propiedad Request :

var sut = new DemoController(someStupRepository, new DeviceDetection("foo"));

Puede hacer esto en, por ejemplo, una prueba unitaria.

Cuando compone DemoController en su aplicación, pasa request.ServerVariables["HTTP_X_REWRITE_URL"].ToString() a DeviceDetection . Puede obtener la variable de request del argumento CreateController de CreateController .

Esta pregunta ya tiene una respuesta aquí:

public class DemoController : Controller { private readonly ICommonOperationsRepository _commonRepo; public DemoController (ICommonOperationsRepository commonRepo) { _commonRepo = commonRepo; } public ActionResult Default() { var model = new DemoModel(); try { **DeviceDetection dd = new DeviceDetection(Request.ServerVariables["HTTP_X_REWRITE_URL"].ToString()); dd.DetectDevice();** model.ListTopListing.AddRange(_commonRepo.GetListings()); } catch (Exception ex) { ExceptionHandler objErr = new ExceptionHandler(ex, "DemoController .Default()/n Exception : " + ex.Message); objErr.LogException(); } return View(model); } }

Problema: DeviceDetection tiene una dependencia concreta aquí, así que no puedo probar mi controlador unitariamente. No quiero simular la solicitud de Http ya que solo quiero probar el controlador y no el módulo DeviceDetection.

¿Cómo puedo simular / evitar el acceso a esto (Request.ServerVariables["HTTP_X_REWRITE_URL"].ToString())

que está causando todos los problemas.


Para responder a su pregunta, necesita lo siguiente en su prueba:

var requestBase = new Mock<HttpRequestBase>(); requestBase.Setup(r => r.ServerVariables) .Returns(new NameValueCollection { {"HTTP_X_REWRITE_URL", "your url"} }); var httpContext = new Mock<HttpContextBase>(); httpContext.Setup(x => x.Request).Returns(requestBase.Object); var ctrCtx = new Mock<ControllerContext>(); ctrCtx.Setup(x => x.HttpContext).Returns(httpContext.Object); demoController.ControllerContext = ctrCtx.Object;

Sin embargo, como @Mark sugirió que no necesita crear una instancia concreta de DeviceDetection dentro de su acción, necesita inyectarla. Pero en lugar de inyectar una instancia concreta, es mejor IDeviceDetector en la interfaz IDeviceDetector e inyectar esta abstracción.

Daré una serie de ventajas:

  1. Su acción no depende de la implementación de DeviceDetection
  2. Mock<IDeviceDetection> permite plantear excepciones en la configuración, para probar el manejo de excepciones de su bloque try-catch .
  3. Puede afirmar que el método DetectDevice() se llama

Otra sugerencia: nunca use try{} catch(Exception ex){} , debería capturar solo aquellas excepciones que puede manejar. Como no sabe qué tipo de excepción se puede lanzar y cómo manejarla de manera efectiva, por ejemplo, puede ser OutOfMemoryException . Este artículo puede brindarle ideas básicas sobre las diferentes formas de manejar la excepción en MVC.

ACTUALIZACIÓN: Como veo, estás usando Unity como contenedor IoC. Unity tiene la posibilidad de inyectar parámetros de constructor . Así que de nuevo necesita extraer una interfaz de IDeviceDetector digamos IDeviceDetector . Registralo

container.RegisterType<IDeviceDetector, DeviceDetector>(new InjectionConstructor( HttpContext.Current.Request.ServerVariables["HTTP_X_REWRITE_URL"].ToString()));

Registre DeviceDetector con TransientLifetimeManager .

Entonces tu controlador debería verse como

public class DemoController : Controller { private readonly ICommonOperationsRepository _commonRepo; private readonly IDeviceDetection _deviceDetection; public DemoController ( ICommonOperationsRepository commonRepo, IDeviceDetection deviceDetection) { _commonRepo = commonRepo; _deviceDetection = deviceDetection; } public ActionResult Default() { var model = new DemoModel(); _deviceDetection.DetectDevice(); model.ListTopListing.AddRange(_commonRepo.GetListings()); return View(model); } }

Tenga en cuenta que, en este caso, debe escribir pruebas unitarias para su contenedor Unity para verificar que sus inyecciones se resuelvan correctamente. La prueba de su unidad puede verse así:

[TestMethod] public void Test() { var repository = new Mock<ICommonOperationsRepository>(); var deviceDetection = new Mock<IDeviceDetection>(); var controller = new DemoController(repository.Object, deviceDetection.Object); controller.Default(); deviceDetection.Verify(x => x.DetectDevice(), Times.Once()); }