Test di HtmlHelpers in ASP.NET MVC
-
06-07-2019 - |
Domanda
Esiste un modo per (unità) testare i miei HtmlHelpers? Nel caso in cui mi piacerebbe avere un controllo personalizzato (reso da HtmlHelper) e conosco i requisiti per quel controllo come potrei scrivere prima i test - e poi scrivere il codice? C'è un modo specifico (carino) per farlo?
Vale la pena?
Soluzione
Il problema principale è che devi deridere HtmlHelper perché potresti utilizzare i metodi dell'helper per ottenere percorsi o valori o restituire il risultato di un altro metodo di estensione. La classe HtmlHelper ha molte proprietà e alcune piuttosto complesse come ViewContext o l'attuale Controller.
Questo post di Ben Hart spiega come creare un tale mock con Moq. Può essere facilmente tradotto in un altro framework simulato.
Questa è la mia versione di Rhino Mocks adattata ai cambiamenti nel MVC Framework. Non è completamente testato ma funziona per me ma non aspettarti risultati perfetti:
public static HtmlHelper CreateHtmlHelper(ViewDataDictionary viewData)
{
var mocks = new MockRepository();
var cc = mocks.DynamicMock<ControllerContext>(
mocks.DynamicMock<HttpContextBase>(),
new RouteData(),
mocks.DynamicMock<ControllerBase>());
var mockViewContext = mocks.DynamicMock<ViewContext>(
cc,
mocks.DynamicMock<IView>(),
viewData,
new TempDataDictionary());
var mockViewDataContainer = mocks.DynamicMock<IViewDataContainer>();
mockViewDataContainer.Expect(v => v.ViewData).Return(viewData);
return new HtmlHelper(mockViewContext, mockViewDataContainer);
}
Altri suggerimenti
Se qualcuno sta cercando come creare HtmlHelper<T>
(questo è quello che stavo cercando), ecco un'implementazione che potrebbe aiutare - il mio tipo è una classe chiamata Model:
public static HtmlHelper<Model> CreateHtmlHelper()
{
ViewDataDictionary vd = new ViewDataDictionary(new Model());
var controllerContext = new ControllerContext(new Mock<HttpContextBase>().Object,
new RouteData(),
new Mock<ControllerBase>().Object);
var viewContext = new ViewContext(controllerContext, new Mock<IView>().Object, vd, new TempDataDictionary(), new Mock<TextWriter>().Object);
var mockViewDataContainer = new Mock<IViewDataContainer>();
mockViewDataContainer.Setup(v => v.ViewData).Returns(vd);
return new HtmlHelper<Model>(viewContext, mockViewDataContainer.Object);
}
O un'implementazione più generica:
public HtmlHelper<T> CreateHtmlHelper<T>() where T : new()
{
var vd = new ViewDataDictionary(new T());
var controllerContext = new ControllerContext(new Mock<HttpContextBase>().Object,
new RouteData(),
new Mock<ControllerBase>().Object);
var viewContext = new ViewContext(controllerContext, new Mock<IView>().Object, vd, new TempDataDictionary(), new Mock<TextWriter>().Object);
var mockViewDataContainer = new Mock<IViewDataContainer>();
mockViewDataContainer.Setup(v => v.ViewData).Returns(vd);
return new HtmlHelper<T>(viewContext, mockViewDataContainer.Object);
}
Sto creando un helper personalizzato, e questo è il codice che sto usando per deridere httphelper con Moq e ASP MVC 2. Sto anche passando come parametro una simulazione di HttpRequestBase. Puoi rimuoverlo se non ti serve
public static HtmlHelper CreateHtmlHelper(ViewDataDictionary viewData, Mock requestMock) { var contextBaseMock = new Mock(); contextBaseMock.SetupGet(cb => cb.Request).Returns(requestMock.Object); var cc = new ControllerContext(contextBaseMock.Object, new RouteData(), new Mock().Object); var vctx = new ViewContext( cc, new Mock().Object, viewData, new TempDataDictionary(), new HtmlTextWriter(new StreamWriter(new MemoryStream()))); var mockViewDataContainer = new Mock(); mockViewDataContainer.SetupGet(v => v.ViewData).Returns(viewData); return new HtmlHelper(vctx, mockViewDataContainer.Object); }