UNIT TESTING CONTROLLERS AND ACTIONS

Một phần của tài liệu Giáo trình lập trình ASP.NET Apress pro ASP NET MVC3 framework pre release (Trang 369 - 374)

Many parts of the MVC framework are designed to facilitate unit testing and this is especially true for actions and controllers. There are a few reasons for this. The first is that we can test them outside of a web server – the context objects are accessed through their base classes (such as HttpRequestBase) which are easy to mock.

Secondly, we don’t need to parse any HTML to test the result of an action method – we can inspect the ActionResult object that is returned to us to ensure that we received the expected result.

The third reason is that we don’t have to simulate client requests. The MVC framework model binding system allows us to write action methods that receive input as method parameters – so to test an action method we simply have to call the action method directly and provide the parameter values that interest us. We will show you how to create unit tests for the different kinds of action result as we go through this chapter.

Don’t forget that unit testing isn’t the complete story – complex behaviors in an application arise when action methods are called in sequence. Unit testing is best combined with other testing approaches.

We can use the RedirectResult class by creating a new instance and returning it from our action method. Listing 9 shows our DerivedController class updated to have two action methods, one of which uses RedirectResult to redirect requests to the other.

Listing 9. Using the RedirectResult class using System.Web.Mvc;

namespace ControllersAndActions.Controllers { public class DerivedController : Controller {

public void Index() {

string controller = (string)RouteData.Values["controller"];

string action = (string)RouteData.Values["action"];

Response.Write(

string.Format("Controller: {0}, Action: {1}", controller, action));

}

public ActionResult Redirect() {

return new RedirectResult("/Derived/Index");

} } }

If we start the application and navigate to /Derived/Redirect, our browser will be redirected to /Derived/Index. To make our code simpler, the Controller class includes

convenience methods for generating different kinds of ActionResult. So, for example, we can achieve the effect in Listing 9 by returning the returning the result of the Redirect method, as shown in Listing 10.

Listing 10. Using a Controller convenience method for creating an action result public ActionResult Redirect() {

return Redirect("/Derived/Index");

}

There is nothing in the action result system which is very complex, but we end up with simpler, cleaner and more consistent code. And we can unit test our action methods very easily

– in the case of a redirection, for example, we can simply check that the action method returns an instance of RedirectResult and that the Url property contains the target we expect.

The MVC framework contains a number of built-in action result types, which are shown in Figure 2 - all of these types are derived from ActionResult and many of them have convenient helper methods in the Controller class.

Table 2. Built-in ActionResult types

Type Description Controller Helper Methods

ViewResult Renders the specified or default view

template.

View

PartialViewResult Renders the specified or default partial view template.

PartialView

RedirectToRouteResult Issues an HTTP 301 or 302 redirection to an action method or specific route entry, generating a URL according to your routing configuration.

RedirectToAction

RedirectToActionPermanent RedirectToRoute

RedirectToRoutePermanent

RedirectResult Issues an HTTP 301 or 302 redirection to a specific URL.

Redirect

RedirectPermanent ContentResult Returns raw textual data to the browser,

optionally setting a content-type header.

Content

FileResult Transmits binary data (such as a file from disk or a byte array in memory) directly to the browser.

File

JsonResult Serializes a .NET object in JSON format and sends it as the response.

Json

JavaScriptResult Sends a snippet of JavaScript source code that should be executed by the browser. This is only intended for use in Ajax scenarios (described in Chapter 19).

JavaScript

HttpUnauthorizedResult Sets the response HTTP status code to 401

(meaning “not authorized”), which causes None

the active authentication mechanism (Forms Authentication or Windows Authentication) to ask the visitor to log in.

HttpNotFoundResult Returns a HTTP 404 not found error HttpNotFound

HttpStatusCodeResult Returns a specified HTTP code None

EmptyResult Does nothing None

In the following sections, we’ll show you how each of these results can be used and how to can create and use a custom action result.

Returning HTML by Rendering a View

The most common kind of response from an action method is to generate HTML and send it to the browser. When using the action result system, we do this by creating an instance of the ViewResult class that specifies the view we want rendered in order to generate the HTML, as demonstrated in Listing 11.

Listing 11. Specifying a view to be rendered using ViewResult using System.Web.Mvc;

namespace ControllersAndActions.Controllers { public class ExampleController : Controller { public ViewResult Index() {

return View("Homepage");

} } }

In this listing, we use the View helper method to create an instance of the ViewResult class, which is then returned as the result of the action method.

Tip Notice that that the return type is ViewResult. The method would compile and work just as well if we have specified the more general ActionResult type and, in fact, some MVC programmers will also define the result of the every action methods to be ActionResult, even when they know it will always return a more specific type. We prefer to return the most specific type we know the method will return,

which is a general object-oriented convention. We have been particularly diligent in this practice in the examples that follow to make it clear how you can use each result type.

We specify the view we want rendered using the parameter to the View method - in this example, we have specified the Homepage view.

Tip We could have created the ViewResult object explicitly, (i.e. return new ViewResult { ViewName = "Homepage" };). This is a perfectly acceptable approach, but we prefer to use the convenient helper methods defined by the Controller class.

When the MVC framework calls the ExecuteResult method of the ViewResult object, a search will begin for the view that we have specified. If we are using areas in our project, then the framework will look in the following locations:

1. /Areas/<AreaName>/Views/<ControllerName>/<ViewName>.aspx 2. /Areas/<AreaName>/Views/<ControllerName>/<ViewName>.ascx 3. /Areas/<AreaName>/Views/Shared/<ViewName>.aspx

4. /Areas/<AreaName>/Views/Shared/<ViewName>.ascx

5. /Areas/<AreaName>/Views/<ControllerName>/<ViewName>.cshtml 6. /Areas/<AreaName>/Views/<ControllerName>/<ViewName>.vbhtml 7. /Areas/<AreaName>/Views/Shared/<ViewName>.cshtml

8. /Areas/<AreaName>/Views/Shared/<ViewName>.vbhtml

You can see from the list that the framework looks for views that have been created for the legacy ASPX view engine (the .aspx and .ascx file extensions), even though we specific Razor when we created the project. The framework also looks for C# and Visual Basic .NET Razor templates (the .cshtml files are the C# ones and .vbhtml are Visual Basic – the Razor syntax is the same in these files, but the code fragments are, as the names suggest, in different languages). The MVC framework checks to see if each of these files exists in turn – as soon as it locates a match, it uses that view to render the result of the action method.

If we are not using areas - or we are using areas but none of the files in the list above have been found – then the framework continues its search, using the following locations:

1. /Views/<ControllerName>/<ViewName>.aspx 2. /Views/<ControllerName>/<ViewName>.ascx 3. /Views/Shared/<ViewName>.aspx

4. /Views/Shared/<ViewName>.ascx

5. /Views/<ControllerName>/<ViewName>.cshtml 6. /Views/<ControllerName>/<ViewName>.vbhtml 7. /Views/Shared/<ViewName>.cshtml

8. /Views/Shared/<ViewName>.vbhtml

Once again, as soon as the MVC framework tests a location and finds a file, then the search stops and the view that has been found is used to render the response to the client.

In Listing 11, we are not using areas, so the first place that the framework will look will be /Views/Example/Index.aspx – notice that the Controller part of the class name is omitted, so that creating a ViewResult in ExampleController leads to a search for a directory called Example.

Một phần của tài liệu Giáo trình lập trình ASP.NET Apress pro ASP NET MVC3 framework pre release (Trang 369 - 374)

Tải bản đầy đủ (PDF)

(603 trang)