UNIT TEST: RETRIEVING IMAGES

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 270 - 292)

We want to make sure that the GetImage method returns the correct MIME type from the

repository and make sure that no data is returned when we request a product ID that doesn’t exist.

Here are the test methods we created:

[TestMethod]

public void Can_Retrieve_Image_Data() { // Arrange - create a Product with image data Product prod = new Product {

ProductID = 2, Name = "Test",

ImageData = new byte[] {}, ImageMimeType = "image/png" };

// Arrange - create the mock repository

Mock<IProductRepository> mock = new Mock<IProductRepository>();

mock.Setup(m => m.Products).Returns(new Product[] { new Product {ProductID = 1, Name = "P1"}, prod,

new Product {ProductID = 3, Name = "P3"}

}.AsQueryable());

// Arrange - create the controller

ProductController target = new ProductController(mock.Object);

// Act - call the GetImage action method ActionResult result = target.GetImage(2);

// Assert

Assert.IsNotNull(result);

Assert.IsInstanceOfType(result, typeof(FileResult));

Assert.AreEqual(prod.ImageMimeType, ((FileResult)result).ContentType);

}

[TestMethod]

public void Cannot_Retrieve_Image_Data_For_Invalid_ID() { // Arrange - create the mock repository

Mock<IProductRepository> mock = new Mock<IProductRepository>();

mock.Setup(m => m.Products).Returns(new Product[] {

new Product {ProductID = 1, Name = "P1"}, new Product {ProductID = 2, Name = "P2"}

}.AsQueryable());

// Arrange - create the controller

ProductController target = new ProductController(mock.Object);

// Act - call the GetImage action method ActionResult result = target.GetImage(100);

// Assert

Assert.IsNull(result);

}

When dealing with a valid product ID, we check that we get a FileResult result from the action method and that the content type matches the type in our mock data. The FileResult class doesn’t let us access the binary contents of the file, so we must be satisfied with a less-than-perfect test.

When we request an invalid product ID, we simply check to ensure that the result is null.

The administrator can now upload images for products. You can try this yourself by editing one of the products.

Figure 9-21 shows an example.

Figure 9-21. Adding an image to a product listing

Displaying Product Images

All that remains is to display the images alongside the product description in the product catalog. Edit the Views/Shared/ProductSummary.cshtml view to reflect the changes shown in bold in Listing 9-35.

35. Listing 9-35. Displaying Images in the Product Catalog

@model SportsStore.Domain.Entities.Product

<div class="item">

@if (Model.ImageData != null) {

<div style="float:left;margin-right:20px">

<img width="75" height="75" src="@Url.Action("GetImage", "Product", new { Model.ProductID })" />

</div>

}

<h3>@Model.Name</h3>

@Model.Description <div class="item">

@using(Html.BeginForm("AddToCart", "Cart")) { @Html.HiddenFor(x => x.ProductID)

@Html.Hidden("returnUrl", Request.Url.PathAndQuery) <input type="submit" value="+ Add to cart" />

} </div>

<h4>@Model.Price.ToString("c")</h4>

</div>

With these changes in place, the customers will see images displayed as part of the product description when they browse the catalog, as shown in Figure 9-22.

Figure 9-22. Displaying product images

Summary

In this and the previous two chapters, we have demonstrated how the ASP.NET MVC Framework can be used to create a realistic e-commerce application. This extended example has introduced many of the framework’s key features: controllers, action methods, routing, views, model binding, metadata, validation, layouts, authentication, and more. You have also seen how some of the key technologies related to MVC can be used. These included the Entity Framework, Ninject, Moq, and the Visual Studio support for unit testing.

We have ended up with an application that has a clean, component-oriented architecture that separates out the various concerns, leaving us with a code base that will be easy to extend and maintain. The second part of this book digs deep into each MVC Framework component to give you a complete guide to its capabilities.

We are going to provide some additional context before we start diving into the details of specific MVC Framework features. This chapter gives an overview of the structure and nature of an ASP.NET MVC application, including the default project structure and naming conventions that you must follow.

Working with Visual Studio MVC Projects

When you create a new MVC 3 project, Visual Studio gives you a choice between three starting points: Empty, Internet Application, or Intranet Application, as shown in Figure 10-1.

Figure 10-1. Choosing the initial configuration of an MVC 3 project

Note The New ASP.NET MVC 3 Project dialog box (Figure 10-1) has a Use HTML5 semantic markup checkbox. Microsoft has started the process of adding HTML5 support to Visual Studio. We have chosen to ignore HTML5 in this book. The MVC Framework is agnostic toward the version of HTML used in a project. HTML5 is a topic in its own right. If you are interested in learning more about HTML5, consider reading Adam’s The Definitive Guide to HTML5, also published by Apress.Pro HTML5 Programming by Peter Lubbers, Brian Albers, and Frank Salim (Apress, 2010).

The Internet Application and Intranet Application templates fill out the project to give a more complete starting point, using different authentication mechanisms that are suited to Internet and intranet applications (see Chapter 22 for details on both authentication systems).

Tip When using the Internet Application or Intranet Application template, you can also elect to create a unit test project as part of the Visual Studio solution. This is another convenience, since you can also create a test project yourself, as we did in Chapter 7.

You can see the difference between these three startup options in Figure 10-2, which shows the initial project set up for these three templates.

The templates create projects that have a common structure. Some of the project items have special roles, which are hard-coded into ASP.NET or the MVC Framework. Others are subject to naming conventions. We have described each of these files and folders in Table 10-1.

Table 10-1. Summary of MVC 3 Project Items

Folder or File Description Notes

/App_Data This directory is where you put private

data, such as XML files or databases if IIS will not serve the contents of this

/bin application is placed here, along with any referenced assemblies that are not in the GAC.

directory. You won’t see the bin directory in the Solution Explorer window unless you click the Show All Files button. Since these are binary files generated on compilation, you should not normally store them in source control.

/Content This is where you put static content such as CSS files and images.

This is a convention but not required. You can put your static content anywhere that suits you.

/Controllers This is where you put your controller classes.

This is a convention. You can put your controller classes anywhere you like, because they are all compiled into the same assembly.

/Models This is where you put your view model and domain model classes, although all but the simplest applications benefit from defining the domain model in a dedicated project, as we demonstrated for

SportsStore.

This is a convention. You can define your model classes anywhere in the project or in a separate project.

/Scripts This directory is intended to hold the JavaScript libraries for your application.

Visual Studio adds the libraries for jQuery and Microsoft AJAX helpers by default.

This is a convention. You can put script files in any location, as they are really just another type of static content.

/Views This directory holds views and partial views, usually grouped together in folders named after the controller with which they are associated.

/Views/Shared This directory holds layouts and views which are not specific to a single controller.

The /Views/Web.config file prevents IIS from serving the content of these directories.

Views must be rendered through an action method.

/Views/Web.config This is not the configuration file for your application. It contains the configuration required to make views work with ASP.NET and prevents views from being served by IIS.

any code to run on application initialization or shutdown, or when unhandled exceptions occur.

/Web.config This is the configuration file for your application. We’ll explain more about its role later in the chapter.

The Web.config file has the same role in an MVC application as it does in a Web Forms application.

Note As you’ll see in Chapter 23, an MVC application is deployed by copying the folder structure to your web server. For security reasons, IIS won’t serve files whose full paths contain Web.config, bin, App_code, App_GlobalResources, App_LocalResources, App_WebReferences, App_Data, or App_Browsers. IIS will also filter out requests for .asax, .ascx, .sitemap, .resx, .mdb, .mdf, .ldf, .csproj, and various other file name extensions. If you do decide to restructure your project, you must be sure not to use these names and extensions in your URLs.

Table 10-2 describes the folders and files that have special meanings if they exist in your MVC 3 project.

Table 10- 2. Summary of Optional MVC 3 Project Items Folder or File Description

/Areas Areas are a way of partitioning a large application into smaller pieces. We’ll explain how areas work in Chapter 11.

/App_GlobalResources /App_LocalResources

These contain resource files used for localizing Web Forms pages.

/App_Browsers This folder contains .browser XML files that describe how to identify specific web browsers, and what such browsers are capable of (whether they support JavaScript, for example).

/App_Themes This folder contains Web Forms themes (including .skin files), which influence how Web Forms controls are rendered.

Using the Internet and Intranet Application Controllers

Figure 10-2 shows that the Internet Application and Intranet Application templates add some default controllers, views, layouts, and view models. Quite a bit of functionality is included, especially in projects created using the Intranet Application template.

The HomeController(present in both the Internet Application and Intranet Application templates) can render a Home page and an About page. These pages are generated with the default layout, which uses a soothing blue- themed CSS file.

The Internet Application template also includes AccountController, which allows visitors to register and log on.

It uses forms authentication to keep track of whether you’re logged in, and it uses the core ASP.NET membership facility (which we discuss in Chapter 22) to record the list of registered users. The membership facility will try to create a SQL Server Express file-based database on the fly in your /App_Data folder the first time anyone tries to register or log in. This will fail, after a long pause, if you don’t have SQL Server Express installed and running.

AccountController also has actions and views that let registered users change their passwords. (The Intranet Application template omits AccountController because accounts and passwords are expected to be managed through a Windows domain/Active Directory infrastructure.)

The default controllers and views can be useful to get your project started, but we tend to use the Empty template so that our application contains only the items that we need.

Understanding MVC Conventions

There are two kinds of conventions in an MVC project. The first kind are really just suggestions as to how you might like to structure your project. For example, it is conventional to put your JavaScript files in the Scripts folder. This is where other MVC developers would expect to find them, and where Visual Studio puts the initial JavaScript files for a new MVC project. But you are free to rename the Scripts folder, or remove it entirely, and put your scripts anywhere you like. That wouldn’t prevent the MVC Framework from running your application.

The other kind of convention arises from the principle of convention over configuration, which was one of the main selling points that made Ruby on Rails so popular. Convention over configuration means that you don’t need to explicitly configure associations between controllers and their views. You just follow a certain naming convention, and everything works. There is less flexibility in changing your project structure when dealing with this kind of convention. The following sections explain the conventions that are used in place of configuration.

Tip All of the conventions can be changed using a custom view engine, which we cover in Chapter 15.

When referencing a controller from an MVC route or an HTML helper method, you specify the first part of the name (such as Product), and the DefaultControllerFactory class automatically appends Controller to the name and starts looking for the controller class. You can change this behavior by creating your own implementation of the IControllerFactory interface, which we describe in Chapter 14.

Following Conventions for Views

Views and partial views should go into the folder /Views/Controllername. For example, a view associated with the ProductController class would go in the /Views/Product folder.

Note Notice that we omit the Controller part of the class from the Views folder; we use the folder /Views/Product, not /Views/ProductController. This may seem counterintuitive at first, but it quickly becomes second nature.

The MVC Framework expects that the default view for an action method should be named after that method . For example, the view associated with an action method called List should be called List.cshtml (or List.aspx if you are using the legacy ASPX view engine). Thus, for the List action method in the ProductController class, the default view is expected to be /Views/Product/List.cshtml.

The default view is used when you return the result of calling the View method in an action method, like this:

return View();

You can specify a different view by name, like this:

return View("MyOtherView");

Notice that we don’t include the file name extension or the path to the view. The MVC Framework will try to find the view using the file name extensions for the installed view engines (Razor and the ASPX engine by default).

When looking for a view, the MVC Framework looks in the folder named after the controller and then in the /Views/Shared folder. This means that we can put views that will be used by more than one controller in the /Views/Shared folder and rely on the framework to find them.

Following Conventions for Layouts

The naming convention for layouts is to prefix the file with an underscore (_) character (as we explained in Chapter 9, this originates from WebMatrix, which also uses Razor), and layout files are placed in the /Views/Shared folder.

Visual Studio creates a layout called _Layout.cshtml as part of the initial project template. This layout is applied to all views by default, through the /Views/_ViewStart.cshtml file, which we discussed in Chapter 5.

If you don’t want the default layout applied to views, you can change the settings in _ViewStart.cshtml (or delete the file entirely) to specify another layout in the view, like this:

Or you can disable any layout for a given view, like this:

@{

Layout = null;

}

Debugging MVC Applications

You can debug an ASP.NET MVC application in exactly the same way as you debug an ASP.NET Web Forms application. The Visual Studio debugger is a powerful and flexible tool, with many features and uses. We can only scratch the surface in this book. We will show you how to set up the debugger, create breakpoints, and run the debugger on your application and unit tests.

Creating the Project

To demonstrate using the debugger, we have created a new MVC 3 project using the Internet Application template.

This gives us the initial controller and views to use. We have called our project DebuggingDemo and checked the option to create a unit test project, as shown in Figure 10-3.

Launching the Visual Studio Debugger

Before we can debug an MVC application, we should check our configuration in Visual Studio. We want to compile our C# classes (such as controllers and domain model entities) in debug mode. The menu for setting this option is shown in Figure10-4; Debug is the default.

Figure 10-4. Selecting the Debug configuration

Figure 10-5. Enabling debugging in an MVC application

If you select the option to modify the Web.config file, the compilation section will be updated so that the value of the debug attribute is true, as shown in Listing 10-1. You can change this value by hand if you prefer.

1. Listing 10-1. Enabling the Debug Attribute in the Web.config File

<configuration>

...

<system.web>

<compilation debug="true" targetFramework="4.0">

...

</compilation>

</system.web>

</configuration>

CautionDo not deploy your application to a production server without disabling the debug settings. We explain why this is, and how you can automate this change as part of your deployment process, in Chapter 23.

At this point, your application will be started and displayed in a new browser window. The debugger will be attached to your application, but you won’t notice any difference until the debugger breaks (we explain what this means in the next section). To stop the debugger, select Stop Debugging from the Visual Studio Debug menu.

control the state of the application. Breaks occur for two main reasons: when a breakpoint is reached and when an unhandled exception arises. You’ll see examples of both in the following sections.

Tip You can manually break the debugger by selecting Break All from the Visual Studio Debug menu while the debugger is running.

Using Breakpoints

A breakpoint is an instruction that tells the debugger to halt execution of the application and hand control to the programmer. At this point, you can inspect the state of the application and see what is happening. To demonstrate a breakpoint, we have added some statements to the Index method of the HomeController class, as shown in Listing 10-2.

2. Listing 10-2. Additional Statements in the HomeController Class

using System.Web.Mvc;

namespace DebuggingDemo.Controllers { public class HomeController : Controller { public ActionResult Index() {

int firstVal = 10;

int secondVal = 5;

int result = firstVal / secondVal;

ViewBag.Message = "Welcome to ASP.NET MVC!";

return View(result);

}

public ActionResult About() { return View();

} } }

These statements don’t do anything interesting. We’ve included them just so we can demonstrate some of the debugger features. For this demonstration, we want to add a breakpoint for the statement that sets a value for the ViewBag.Message property.

Figure 10-6. Adding a breakpoint

If we start the application with the debugger (by selecting Start Debugging from the Debug menu), the application will run until the statement that has the breakpoint is reached, at which point the debugger will transfer control back to us, as shown in Figure 10-7.

Figure 10-7. Hitting a breakpoint

Note A breakpoint is triggered only when the statement it is associated with is executed. Our example breakpoint was reached as soon as we started the application because it is inside the action method that is called when a request for the default URL is received. If you place a breakpoint inside another action method, you must use the browser to request a URL associated with that method. This can mean working with the application in the way a user would or navigating directly to the URL in the browser window.

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 270 - 292)

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

(603 trang)