ASP.NET MVC: Pengontrol pengujian unit yang menggunakan UrlHelper

170

Salah satu tindakan pengontrol saya, yang dipanggil dalam permintaan Ajax, adalah mengembalikan URL ke sisi klien sehingga dapat melakukan pengalihan. Saya menggunakan Url.RouteUrl(..)dan selama pengujian unit saya gagal karena Controller.Urlparameter tidak diisi sebelumnya.

Saya mencoba banyak hal, antara lain berusaha untuk rintisan UrlHelper(yang gagal), secara manual membuat UrlHelperdengan RequestContextyang memiliki rintisan HttpContextBase(yang gagal pada RouteCollection.GetUrlWithApplicationPathpanggilan).

Saya telah mencari Google tetapi hampir tidak menemukan apa-apa tentang masalah ini. Apakah saya melakukan sesuatu yang sangat bodoh menggunakan Url.RouteUrltindakan Controller saya? Apakah ada cara yang lebih mudah?

Untuk membuatnya lebih buruk, saya ingin dapat menguji URL yang dikembalikan dalam tes unit saya - sebenarnya saya hanya tertarik mengetahui bahwa itu mengarahkan ke rute yang benar, tetapi karena saya mengembalikan URL alih-alih rute, saya ingin mengontrol URL yang diselesaikan (mis. dengan menggunakan stubbed RouteCollection) - tetapi saya akan senang mendapatkan lulus pengujian saya untuk memulai.

efdee
sumber

Jawaban:

202

Berikut ini adalah salah satu tes saya (xUnit + Moq) hanya untuk kasus serupa (menggunakan Url.RouteUrl di controller)

Semoga ini membantu:

var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);

var request = new Mock<HttpRequestBase>(MockBehavior.Strict);
request.SetupGet(x => x.ApplicationPath).Returns("/");
request.SetupGet(x => x.Url).Returns(new Uri("http://localhost/a", UriKind.Absolute));
request.SetupGet(x => x.ServerVariables).Returns(new System.Collections.Specialized.NameValueCollection());

var response = new Mock<HttpResponseBase>(MockBehavior.Strict);
response.Setup(x => x.ApplyAppPathModifier("/post1")).Returns("http://localhost/post1");

var context = new Mock<HttpContextBase>(MockBehavior.Strict);
context.SetupGet(x => x.Request).Returns(request.Object);
context.SetupGet(x => x.Response).Returns(response.Object);

var controller = new LinkbackController(dbF.Object);
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
controller.Url = new UrlHelper(new RequestContext(context.Object, new RouteData()), routes);
eu-ge-ne
sumber
2
Untuk saat ini saya pergi dengan solusi di mana saya mengabstraksikan panggilan ke UrlHelper sehingga saya dapat mencegatnya. Namun, terima kasih atas cuplikan Anda, ini akan menghemat banyak waktu untuk mencari tahu bagaimana cara mengejek Request / Response / ControllerContext dengan benar.
efdee
Terima kasih atas jawabannya @ eu-ge-ne, itu banyak membantu saya. Saya telah menyertakan beberapa pengaturan moq lagi untuk menggunakan parameter formcollection yang digunakan oleh UpdateModel
jebcrum
16
+1 luar biasa. Meskipun sebuah tip: Saya menggunakan ini sebagai MockHelper dan mengubah respons. Pengaturan untuk ApplyAppPathModifier untuk ini: response.Setup (x => x.ApplyAppPathModifier (Moq.It.IsAny <String> ())) Pengembalian ((url String) ) => url); Ini jelek, tapi saya mendapatkan objek berseri kembali dalam bentuk disandikan url, alih-alih hardcoding nilai yang dikembalikan.
eduncan911
Itu sebagian bekerja untuk saya. Adakah ide mengapa saya mendapatkan Controller / bukannya Controller / Action? Tes saya gagal karena mereka tidak persis sama namun saya mendaftarkan nilai-nilai routing yang sama. Sangat aneh ...
Nick
3
Bagian ApplyAppPathModifierini adalah bagian penting untuk UrlHelper
Chris S
37

Implementasi yang dimodifikasi dari eu-ge-ne. Yang ini mengembalikan tautan yang dibuat berdasarkan rute yang ditentukan dalam aplikasi. Contoh eu-ge-ne selalu mengembalikan respons tetap. Pendekatan di bawah ini akan memungkinkan Anda untuk menguji bahwa tindakan / pengontrol yang benar dan informasi rute dilewatkan ke dalam UrlHelper - yang merupakan apa yang Anda inginkan jika Anda menguji panggilan ke UrlHelper.

var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var response = new Mock<HttpResponseBase>();
var session = new Mock<HttpSessionStateBase>();
var server = new Mock<HttpServerUtilityBase>();

context.Setup(ctx => ctx.Request).Returns(request.Object);
context.Setup(ctx => ctx.Response).Returns(response.Object);
context.Setup(ctx => ctx.Session).Returns(session.Object);
context.Setup(ctx => ctx.Server).Returns(server.Object);

request.SetupGet(x => x.ApplicationPath).Returns("/");
request.SetupGet(x => x.Url).Returns(new Uri("http://localhost/a", UriKind.Absolute));
request.SetupGet(x => x.ServerVariables).Returns(new NameValueCollection());

response.Setup(x => x.ApplyAppPathModifier(It.IsAny<string>())).Returns<string>(x => x);

context.SetupGet(x => x.Request).Returns(request.Object);
context.SetupGet(x => x.Response).Returns(response.Object);

var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
var helper = new UrlHelper(new RequestContext(context.Object, new RouteData()), routes);
Steven Pena
sumber
12

Posting ini mungkin berguna jika Anda ingin mengejek kelas HttpContextBase.

http://www.hanselman.com/blog/ASPNETMVCSessionAtMix08TDDAndMvcMockHelpers.aspx

Gerardo Contijoch
sumber
Keren, ini membantu saya, meskipun saya harus menambahkan beberapa kode tambahan ke metode FakeHttpContext untuk menghentikan helper meledakkan: context.Setup (ctx => ctx.Request.ApplicationPath) .Returns ("/ AntiBlowup"); Saya juga refactored kode sehingga menggunakan sintaks Setup () yang lebih baru. Terima kasih.
RichardOD
2

Membangun jawaban oleh @ eu-ge-ne yang banyak membantu saya:

Saya memiliki ActionResult yang melakukan redirect dan juga panggilan UpdateModel dengan parameter FormCollection. Agar UpdateModel () berfungsi, saya harus menambahkan ini ke HttpRequestBase Mocked saya:

FormCollection collection = new FormCollection();
collection["KeyName"] = "KeyValue";

request.Setup(x => x.Form).Returns(collection);
request.Setup(x => x.QueryString).Returns(new NameValueCollection());

Untuk menguji apakah URL yang dialihkan benar, Anda dapat melakukan hal berikut:

RedirectResult result = controller.ActionName(modelToSubmit, collection) as RedirectResult;
Assert.AreEqual("/Expected/URL", result.Url);
jebcrum
sumber