Home MVC Storefront

MVC Storefront, Part 8: Testing Controllers, Iteration 1

In this episode I finally ready up the demo, setting up my CatalogController to work with my test repository, and using the DRY principle with Helpers so I don't repeat view logic.

Previously, On The MVC Storefront

  • Part 1: Architectural Discussion and Overview. I cover the initial architecture here, in part 1, where I discuss the Repository Pattern with Ayende Rahien and Steve Harman. I also ask Phil Haack what's first: The Test Chicken or the Model Egg.
  • Part 2: The Repository Pattern. I walk through our Repository implementation and write out the first main set of unit tests. I also  structure up the initial service layer methods and interfaces.
  • Part 3: Pipes and Filters: I discuss the feedback from Parts 1 and 2, and then dive into the Pipes and Filters implementation on the IQueryable repository.
  • Part 4: Linq To Sql Spike. I create a "spike" - a bit of functionality to test a thought or inspiration - to make sure that Linq To Sql will work for my data access pattern.
  • Part 5: Globalization. I work with Damien Guard to refactor the database to deal with test data and globalization issues.
  • Part 6: Finishing The Repository, and Initial UI Work. I talk with Jon Galloway about CSS strategies and begin working in the Front end of the application
  • Part 7: Routing and UI Work. I talk with Scott Hanselman about how to setup Routing, and then I implement the user interface.

It's All Code Baby
Many people have asked for more code - and you got it. There's 30 minutes of nothing but TDD, Code, and MVC UI stuff here. It's almost completely work within the website, and some refactoring along the way.

Download Part 8 Here (30 minutes, 56M).

What did you think of the demo? What do you want to see next? Your feedback is critical...

Technorati Tags:
George avatar
George says:
Friday, May 02, 2008

Hi Rob,

Thanks for this mvc storefront project. Now that you are starting from scratch, I hope that you can add on some nice features that you can't implement due to the architecture/design/database limitation of the commerce starter kit.

I just downloaded the latest files, and it looks like globabl.asax, web.config and default.aspx are missing.


Ryan avatar
Ryan says:
Friday, May 02, 2008

Confirming missing files in the latest release on codeplex.


Zack Owens avatar
Zack Owens says:
Friday, May 02, 2008

Rob,

Got a "down-the-line" type of question. Are you going to provide a rich API in a MVC-type way? I know that an eCommerce site should contain the usual "GetTheProducts" for product indexes, but could you do a nice API that works off the CatelogRepository in an MVC fassion without actually writing the controller methods? Just a thought... might play with this idea..

Thanks

Zack


Paul Linton avatar
Paul Linton says:
Saturday, May 03, 2008

Rob,

Many, many thanks for this whole series. I have been trying to tackle everything you have been talking about and getting rather tangled.

I like the method you haveused to separate your domain model from the database schema. Nicely, pragmatic. My 2c on the naming is as an electrical engineer, one way that we solve impedence mismatch issues is with a transformer so maybe your Repository+ could be called "Transformer" and not upset the folks who are protecting the name "Repository". Although I wouldn't be surprised if there is already a pattern called Transformer.

The web site I am developing is intended to support multiple languages but I have used a different approach to you in the db schema (the details don't matter). The fantastic thing is that your model would not need to be changed if you were to change to my DB schema.

In another project I have to work with a legacy DB. After watching this series I am confident that I can build a clean Domain Model and use a Transformer/repository+ to talk to my legacy DB. Yippee. And test without hitting the database.

My vote for the next area to tackle would be writing to the DB. Perhaps a spike to show how that would happen? Pretending to be your client, "I want to know how many times each product is being viewed". No need to be too fancy at this stage, just a simple incrementing counter of total product views so I can see what are the most popular products. I guess you may need to tackle some concurrency issues to solve this as well as the general approach to updating.

Once again, many thanks and keep up the good work.


Martin Nyborg avatar
Martin Nyborg says:
Saturday, May 03, 2008

I would like to see much more about the repository pattern you have build. Validation, dirty tracking and all the stuff you have baked in to SubSonic. And maybe in a winform application with WCF.


Yvan Castonguay avatar
Yvan Castonguay says:
Saturday, May 03, 2008

Hi Rob,

I know this is probably only the first iteration in your project. But what i'd like to see, is how you are going to handle updates to data. I know there will probably be some kind of shopping cart in there which will have to perform changes to the data but...

Perhaps the best way would be to have a content management section in the commerce website where the admin can perform CRUD operations on the repository's data for maintenance.

I don't know about the regular audience of this serie but for my part i am not working too much with ecommerce type of application.

On a side note, i like to thank you for being one of the first to provide a real TDD experience live on the net! it made me realize that too often projects get started as TDD and shifted to TAC (test after code) in some areas after a while. I think it would be nice to have one of you peer at MSFT to do the same with a regular website pr web application project.

Thanks and keep it up!


Roger avatar
Roger says:
Saturday, May 03, 2008

Is it possible to follow along with this project using Visual Web Developer 2008? I've read that, in principal, MVC projects can be coded using VWD, but would it be practical for this project?

Thanks


Paul Linton avatar
Paul Linton says:
Sunday, May 04, 2008

Since you were nit picking your own code can I have a go?

Instead of

data.SubCategory =

data.Categories.WithCategoryName(subcategory);

//catch for bad SubCategory

data.SubCategory = data.SubCategory ??

data.Category.SubCategories[0];

in CatalogController.cs why not go all the way to

data.SubCategory =

data.Categories.WithCategoryName(subcategory) ??

data.Category.SubCategories[0];


Herbrandson avatar
Herbrandson says:
Monday, May 05, 2008

You want to know what I as the customer think? I think the functionality seems good, but the colors and font are all wrong. And, when I view it on my home computer (800 x 600) it doesn't all fit on the screen. Can you have that fixed by tomorrow morning?

Wow, that was really cathartic. Thanks!


firefly avatar
firefly says:
Monday, May 05, 2008

heh Herbrandson what are you doing here man? Did the monkey find a new boss? :)

Rob, So when are you going to slow down a little so the customer can actually review your apps? Myself I've been wondering how you could churning out all the webcast one after another. Not that I mind of course just wish that I have more time to play around with your sample.


Paul Linton avatar
Paul Linton says:
Monday, May 05, 2008

Rob, I have noticed that you have created a static DataContext. The MSDN documentation for DataContext advises

"A DataContext is lightweight and is not expensive to create. A typical LINQ to SQL application creates DataContext instances at method scope or as a member of short-lived classes that represent a logical set of related database operations."

What is your reasoning behind sharing a single DataContext instance?

thanks and keep up the good work.


Yitzchok avatar
Yitzchok says:
Monday, May 05, 2008

Whats with paging on the catalog page?


Nagarajan avatar
Nagarajan says:
Tuesday, May 06, 2008

Any update on Makai?


Mike avatar
Mike says:
Tuesday, May 06, 2008

Yeah, I too would like to know about the static datacontext. I think it's only used for reading right? In that case, for writes you'd instantiate a new datacontext. No change tracking though in that case, I think...


mike avatar
mike says:
Tuesday, May 06, 2008

You know the MVC team. Are they considering a different way to render user controls? I don't think the page should fetch the data. It would be better to have some kind of route you can start up, that will call a separate controller, with a view that will render *into* the page at the location you called the route.


Remmus avatar
Remmus says:
Tuesday, May 06, 2008

Great series so far!!!

A couple of small points, the web project seems to be a playing up, lots of the files aren't there.

Also the latest SQL script seems to be playing up and not running as intended. Finally is it possible to get another script for the data you are using so we can follow along with that too?


Rob Conery avatar
Rob Conery says:
Tuesday, May 06, 2008

@Paul Linton: My reasoning is that I need the Data Context for every query, and I don't need 90% of what it does for the fetches (Object Tracking, transactions, etc). So I made it a Singleton and turned off Object Tracking for perf.

Later on I can refine this by using Pre-defined queries, which will up perf even more :). So, in-essence, I'm following a fairly normal design pattern here.

@Yitzock - no requirement for paging yet, but it does seem pretty standard :).

@mike:

>>>I don't think the page should fetch the data. It would be better to have some kind of route you can start up, that will call a separate controller, with a view that will render *into* the page at the location you called the route<<<

No pages "Fetch" data. The user control excepts it like the other Views.

@Remmus: Yah Tortoise seems to have flipped out on me. I'll fix it with the next commit.


Rasmus Kromann-Larsen avatar
Rasmus Kromann-Larsen says:
Tuesday, May 06, 2008

A minor detail, when making your controllers testable (around 2 minutes into the screencast), you do this:

public CatalogController()

{

_repository = new SqlCatalogRepository();

}

public CatalogController(ICatalogRepository repository)

{

_repository = repository;

}

...

A neat little trick I started using a while back when in these situations to keep the code more DRY is to do this instead:

(around 2 minutes into the screencast), you do this:

public CatalogController() : this(new SqlCatagoryRepository())

{

}

public CatalogController(ICatalogRepository repository) {

_repository = repository;

}

...

I realize it might look like a very small change, but it does give you compile-errors when you go about changing one of the constructors, instead of forgetting one of them and producing weird bugs.

Anyways, thanks for the series - been good so far :-)

- Rasmus.


Rasmus Kromann-Larsen avatar
Rasmus Kromann-Larsen says:
Tuesday, May 06, 2008

Oh and btw.

I noticed that you use RenderUserControl - but you still have the "string" file reference in "blah.ascx".

Couldn't you use the RenderComponent<>() method on the Html helper and have a strongly typed solution instead? Also it seems to give you a front-controller for your user control, which seems more MVC :-)

Saw it on Chris Saintys blog here:

csainty.blogspot.com/.../rendercomponent


Stephen avatar
Stephen says:
Tuesday, May 06, 2008

Rob, whats your experience with IoC, DI? one way or another you've prompted me into taking on TDD (already it feels like an old friend)..

IoC and DI are one of them things like TDD that I'm constantly bugged about..

Maybe when this is done (plus the time you need to get over this :)) you could do one on IoC and DI ;)


Rob Conery avatar
Rob Conery says:
Tuesday, May 06, 2008

@Rasmus - thanks! Good stuff :)

@Stephen - I'll be using DI/IoC in the next few webcasts here. I asked David Hayden to show me nice ways to use Unity, and I'll capture it :).


Ross Neilson avatar
Ross Neilson says:
Tuesday, May 06, 2008

This is a really enjoyable series and is getting better each episode.

My only comment would be some of the asserts in the unit tests. I always try to make them as specific as possible, both to ensure that the code is thoroughly tested and also to make the intention clear.

An example is the CatalogController_IndexMethod_ShouldReturn_Categories_And_Data_For_Parent1 test in CatalogControllerTests. Rather than asserting that Category and SubCategory are not null, I think it might be better to do

Assert.AreEqual("Parent1", data.Category.Name);

Assert.AreEqual("Sub10", data.SubCategory.Name);

Just my two cents' worth.

With regards to future posts, I'd be interested to you build a form which posts data back to the database, validating it along the way. Perhaps a page for users to submit product reviews would fit the bill.

Thanks a lot and keep up the good work.


Dirk Maegh avatar
Dirk Maegh says:
Wednesday, May 07, 2008

How about some view output testing ? I haven't seen anything on this subject anywhere yet, only controller testing, route testing and model testing.

Does nobody test the view output ? This should be your primary test, as this is the acceptance level ?!


Pierre-Jean avatar
Pierre-Jean says:
Wednesday, May 07, 2008

Rob,

This is not the subject for this but do you plan a date for the V3 of subsonic ?

I am so impatient to use it :)

Thanks a lot and thakns for this tutorial


pmw avatar
pmw says:
Wednesday, May 07, 2008

Rob,

Is there going to be an update to the db scripts on codeplex for the recent parts 7/8? The latest db script is named for part 6, and it doesn't execute properly anyway. I think we could also use the actual data as well - could you perhaps include an mdf in the code instead of just scripts?

Thanks,

pmw


Roderik Steenbergen avatar
Roderik Steenbergen says:
Wednesday, May 07, 2008

Definately enjoy the series, it's clear and well cut so thumbs up for your video editing skills amongst other things.

Trying to get my head around TDD myself, i'm really interested in seeing more on the testdriven side. I'm hoping to see a pretty breaking reaction from the customer like: "You do realize there's also something called 'productgroups'. Different customers might be interested in different productgroups so we must be able to show or hide these productgroups based on user preferences". See if this test-driven development (with a close to 100% coverage) paid off...

@Herbrandson: favorite quote of one of my ex-bosses:

"As soon as they (customers) start pixel-f**ing over details, you know you've done a good job overall"

And again, really great series, thanks a lot! Keep em coming


Herbrandson avatar
Herbrandson says:
Thursday, May 08, 2008

@Dirk: I actually wrote an article about that. It's not a perfect solution, but so far I've been really pleased with the results.

<a href="http://www.codeproject.com/KB/aspnet/Unit_Testing_MVC_Views.aspx">http://www.codeproject.com/KB/aspnet/Unit_Testing_MVC_Views.aspx</a>


Leave a Comment


Search Me
Subscribe

Popular Posts
 
My Tweets
  • @codinghorror or just listen to Murray Stree by Sonic Youth with a beer in one hand a cat in another
  • @codinghorror: Listen to Miley Syrus - replace one mind virus with another :)
  • This day brought to you by Sonic Youth.
  • Green lights on my first Linq To SubSonic query :)
  • My patience is gone for today. Wanted to do up cast 10 but my usually thick skin is remarkably thin today.
  About Me



Hi! My name is Rob Conery and I work at Microsoft on the ASP.NET team. I am the Creator of SubSonic and was the Chief Architect of the Commerce Starter Kit (a free, Open Source eCommerce platform for .NET)

I live in Kauai, HI with my family, and when my clients aren't looking, I sometimes write things on my blog (giving away secrets of incalculable value).