Skip to content

Commit e3179db

Browse files
committed
Started adding readme content
1 parent 74032c4 commit e3179db

1 file changed

Lines changed: 121 additions & 1 deletion

File tree

README.md

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,124 @@
1-
ASP.NET MVC Terse Controller Testing
1+
Fluent MVC Testing
22
====================================
33

4+
This library provides a fluent interface for creating terse and expressive tests against ASP.NET MVC controllers.
45

6+
This library is testing framework agnostic, so you can combine it with the testing library of your choice (e.g. NUnit, xUnit, etc.).
7+
8+
The library is compatible with the AAA testing methodology, although it combines the Act and Assert parts together (but you can have other assertions after the Fluent assertion). See the code examples below for more information.
9+
10+
The motivation behind this library is to provide a way to test MVC actions quickly, tersely and maintainably. Most examples I find on MVC controller testing are incredibly verbose, repetitive and time-consuming to write and maintain. Given how quickly you can write controller actions and how simple they are (assuming you are following best practices and keeping them lean) the time to test them generally isn't worth it given you can glance at most of your controller actions and know they are right or wrong instantly. This library aims to make the time to implement the tests inconsequential and then the value your tests are providing is worth it. The other problem that I've noticed with most examples of controller testing is that there are a lot of magic strings being used to test view and action names; this library also aims to utilise the type system to resolve a lot of those magic strings, thus ensuring your tests are more maintainable and require less re-work when you perform major refactoring of your code.
11+
12+
Installation
13+
------------
14+
15+
You can install this library using NuGet into your Test Library; it will automatically reference System.Web and System.Web.Mvc for you.
16+
17+
If you are using ASP.NET MVC 4 then:
18+
19+
Install-Package FluentMVCTesting
20+
21+
If you are using ASP.NET MVC 3 then:
22+
23+
Install-Package FluentMVCTesting.Mvc3
24+
25+
Examples
26+
--------
27+
28+
### Recommended class structure
29+
30+
The following code snippet is an example for how to set up a test class to use FluentMVCTesting using NUnit, this is simply to get you started quickly, in reality you can use it how you like and with any unit testing framework of your choice.
31+
32+
using MyApp.Controllers;
33+
using NUnit.Framework;
34+
using TerseControllerTesting;
35+
36+
namespace MyApp.Tests.Controllers
37+
{
38+
[TestFixture]
39+
class HomeControllerShould
40+
{
41+
private HomeController _controller;
42+
43+
[SetUp]
44+
public void Setup()
45+
{
46+
_controller = new HomeController();
47+
}
48+
49+
[Test]
50+
public void Render_default_view_for_get_to_index()
51+
{
52+
_controller.WithCallTo(c => c.Index()).ShouldRenderDefaultView();
53+
}
54+
}
55+
}
56+
57+
### Basic syntax
58+
59+
As you might have gathered from the previous code snippet the entry point to the library is the `WithCallTo` extension method on the MVC `Controller` class; inside of that method call you simply pass in a lamdba expression that takes the controller to make the action call that you are trying to test. From there you can use intellisense to guide your testing.
60+
61+
### Model binding errors
62+
63+
If you want to test what happens when the default model binding (or indeed any custom model binding or validation that you perform) has left errors in `ModelState` when your action is called you can simply use the `WithModelErrors()` extension method, e.g.:
64+
65+
_controller.WithModelErrors().WithCallTo(c => c.Index()).ShouldRenderDefaultView();
66+
67+
### Child actions
68+
69+
If you want to check that the action being called has the `[ChildActionOnly]` attribute then use `WithCallToChild()` rather than `WithCallTo()`, e.g.:
70+
71+
_controller.WithCallToChild(c => c.Index()).ShouldRenderDefaultView();
72+
73+
### Empty result
74+
75+
_controller.WithCallTo(c => c.Index()).ShouldReturnEmptyResult();
76+
77+
### Redirect to url
78+
79+
_controller.WithCallTo(c => c.Index()).ShouldRedirectTo("http://www.google.com.au/");
80+
81+
### Redirect to route name
82+
83+
_controller.WithCallTo(c => c.Index()).ShouldRedirectToRoute("RouteName");
84+
85+
### Redirect to action within the same controller
86+
87+
There are a number of ways that you can specify this test, depending on the signature of the action you are redirecting to.
88+
89+
If you are redirecting to an action that takes no parameters, or takes a single int parameter, then you can use a method group, which is the tersest specification (you don't need to specify the parameters to the action), e.g. if these are two actions in the controller you are testing:
90+
91+
public ActionResult ActionWithNoParameters()
92+
{
93+
return new EmptyResult();
94+
}
95+
public ActionResult RedirectToActionWithNoParameters()
96+
{
97+
return RedirectToAction("ActionWithNoParameters");
98+
}
99+
100+
Then you can write this test:
101+
102+
_controller.WithCallTo(c => c.RedirectToActionWithNoParameters()).ShouldRedirectTo(c => c.ActionWithNoParameters)
103+
104+
I can explicitly define whatever signatures I want to allow this terser syntax, but obviously the different permutations that are possible are mind-boggling and it wouldn't be helpful for any custom types in your project. Unfortunately, despite best efforts I couldn't figure out a way to generically specify these definitions - if anyone has ideas for how to do this let me know!
105+
106+
If you have 1-3 parameters being passed into the action being redirected to then you can still use the method group, but you need to specify the types being passed into the action, e.g. if you have the following controller action being redirected to:
107+
108+
public ActionResult SomeAction(string param1, int param2, bool param3) {}
109+
110+
Then you can write the following test for the redirect:
111+
112+
_controller.WithCallTo(c => c.Index()).ShouldRedirectTo<string, int, bool>(c => c.SomeAction)
113+
114+
If you have more than three parameters, or you are uncomfortable with that syntax then you can specify a lambda with a call to the action you want and pass in dummy values for the parameters, e.g. for the previous example:
115+
116+
_controller.WithCallTo(c => c.Index()).ShouldRedirectTo(c => c.SomeAction(null, 0, false))
117+
118+
You can also pass through a MethodInfo object against the method you are redirecting to, e.g.:
119+
120+
_controller.WithCallTo(c => c.Index()).ShouldRedirectTo(typeof(HomeController).GetMethod("SomeAction"))
121+
122+
If you use this option (I don't recommend it because it uses a "magic" string so if you change the action name then the string won't change, although at least the test will break because the Method name will no longer be valid; in saying that if you change your parameters more often than the action name this might be a better option) be careful that you don't get an AmbiguousMatchException if there are multiple actions with that name.
123+
124+
At this stage there isn't support for the `[ActionName()]` attribute or simply passing through a string to check against the action name, but if either are important to you feel free to add an issue for this GitHub project and I can add them.

0 commit comments

Comments
 (0)