Home MVC Storefront

MVC Storefront, Part 12: Mocking

In this episode I implement mocking so I can test my Authentication action for my UserController. In order to do this, however, I have to mock up the HttpContext...

Not much else to say here - it's all about Mocking!

Watch it here.

The code is here.

Technorati Tags:
Jamie avatar
Jamie says:
Saturday, May 24, 2008

Am I missing something on why all of this was needed? You created an action called Authenticate with no parameters, then read the username and password from the form. Couldn't you have just created Authenticate(string username, string password) then in your login view had a form with method=post and the framework would wire up those form post items for you and make it not necessary for all of the mocking because you could just call somethinglike Authenticate("testuser", "testpassword") from your test code?

Also, regarding components, will their base controller class be refactored to return a RenderActionResult or something to make it more consistent with normal controllers?

Great series by the way, can't wait for more.


Rob Conery avatar
Rob Conery says:
Sunday, May 25, 2008

Hi Jamie:

>>Couldn't you have just created Authenticate(string username, string password) then in your login view had a form with method=post and the framework would wire up those form post items for you<<<

When you post to an action, you have to supply the arguments to it. As far as I know what you've suggested isn't possible. I would need to post to something like:

/user/authenticate/username/password

You can see straight away why that's a bad idea :).


Jamie avatar
Jamie says:
Sunday, May 25, 2008

Ok, I guess I got that confused with MonoRail where parameters can be filled in by form items. I've use both and sometimes get features confused. Possibly something that could/should be added though?


Robert avatar
Robert says:
Sunday, May 25, 2008

As far as I know is this not correct - form parameters can be mapped to parameters.

If you have a simple form with 2 Input fields ("username", "password") than can access the values in these method: Login(string username, string password) - without HTTP mocking.

(see here: http://forums.asp.net/t/1264070.aspx)


Robert avatar
Robert says:
Sunday, May 25, 2008

Another note: Have a look at the MVC Membership Starter Kit: www.squaredroot.com/.../MVC-Membership-

It´s a really nice implementation :)


Erik avatar
Erik says:
Sunday, May 25, 2008

Sure, It populates the method properties from the form data but It appears to go URL,Default properties, then querystring/form data. So you do lose the abillity to go with your strongly typed Action Links. Unless I am missing something, if you change your method to

public ActionResult Authenticate(string userName, string password, string remember, string returnUrl)

Then you have to change your Form from

<%using (Html.Form<UserController>(x=>x.Authenticate())) {%>

to

<%using (Html.Form<UserController>(x=>x.Authenticate(null,null,null,null))) {%>

But no, this still wont matcha route unless you put default values in route in which case those default values will be used instead of the form values so we actually have to use

<%using (Html.Form("user","Authenticate")) { %>

and it works. Is there actually a way to use the Lambda Expression with form populated method property values?


Keith Woods avatar
Keith Woods says:
Monday, May 26, 2008

I believe you’re ObjectBuilder2 and Unity references in Web.csproj are not pointing to your Dependencies folders (gac perhaps?). I downloaded v6640 and got a build error, the references could not be found. When I re-pointed the references to the dll’s in the dependencies folders I got some type exceptions because the Unity dll is out of date (a class was missing…). Got a copy of that from http://codeplex.com/unity, re-build, referenced and its good to go. Small thing but for newbie’s may waste some time. Great series…


Jason Stangroome avatar
Jason Stangroome says:
Monday, May 26, 2008

Hi Rob,

My comments don't relate directly to episode 12 but I imagine you're more likely to check here.

1. I haven't seen anything in the entire series that would require administrator privileges in VS. Please stop running Visual Studio elevated, you're setting a bad example.

2. ShoppingCart has an .AddItem method with important logic but I believe someShoppingCart.Items.Add(New ShoppingCartItem(...)); is more common and discoverable and will skip important logic if used. How do you intend to address this when another dev, unaware of your AddItem method wants to work with the cart?

3. Your integration tests don't rollback the database to a known consistent state. For example, ShoppingCartIntegrationTests.SqlCartRepository_Can_Add_Product_3() tests a different code path on the first run compared to all subsequent runs. And the remove cart item tests could fail once due to a bug then require a code fix *and* manual DB tidying before they pass again.

4. Related to (3) is the need to have your database represented in source control for new developers or when you lose or break your copy on your local SQL server. How do you remind yourself to put the latest database backup into source control whenever you make changes? Have you considered moving to Data Dude or a hand-rolled approach?

5. You mention in episode 10 that people were asking about Atomic Commit/Unit Of Work and you stated that you feel the Shopping Cart model doesn't require that level of transactional handling. I agree. However, I would like to see some feature added to the Commerce MVC app which would require you to address a Unit Of Work pattern with POCO <=> Linq-to-SQL. I like your model/data-access approach so far and would like to see it go all the way.

Keep up the good work.

Thanks,

--

Jason


Rob Conery avatar
Rob Conery says:
Monday, May 26, 2008

@Keith - oops :) thanks. Yes, the DLLs are outdated and I'm using a GAC reference for Unity 1.1.

@Jason: >>>I haven't seen anything in the entire series that would require administrator privileges in VS<<<

VS requires run as administrator (it will even pop a dialog if you don't). If yo know otherwise - let me know.

In terms of AddItem versus Items.Add - there's not much I can do really, other than to roll my own collection and hide the collection bits. There are many examples of why you don't want to run protected/internal, and while I can figure something out to "protect" the unaware dev, I'll probably run afoul of the more experienced devs who want to use the core methods of IList.

>>>Your integration tests don't rollback the database to a known consistent state<<<

I should probably clarify a little bit but those aren't intended to be final integration tests, despite sitting in an IntegrationTests project :). They started out as spikes. Your point's taken tho.

RE #4 - Personally, I usually have a scripts folder and I make SubSonic script my changes whenever I commit something. For this series I backup when needed (the BAK file is in the data project). No, I don't like external programs to handle this for me - they never seem to do what I want :). I'd rather make SQL do this with a job or something.

RE #5 - I still believe this, but I've changed direction on it (once again - I'm sure I'll do it again). In episode 11 (I think) I reset the CartService/Repositories to do just this - an atomic Save(). Check it out - it's in the source running in a full transaction :).


Rob Conery avatar
Rob Conery says:
Monday, May 26, 2008

@Jason: I wanted to add RE UAC/Good Examples - personally I would never tell anyone to run UAC when developing. It causes more problems then it solves and ultimately, you WILL turn it off, or just run elevated. I'd like to hear your reasoning on why you think people shouldn't run VS elevated...


Firefly avatar
Firefly says:
Monday, May 26, 2008

Hmm... I've always been running VS with a normal account without any problem. Personally myself I wouldn't run anything under the Admin account unless I have to or inside a virtual machine. In fact it's always a nice thing to have a virtual machine for development purposes. You can install whatever software and rollback in a snap.


Rob Conery avatar
Rob Conery says:
Monday, May 26, 2008

You know - it just occurred to me that I'm thinking of VS 2005.

That said, UAC and me are not friends :).


Jason Stangroome avatar
Jason Stangroome says:
Monday, May 26, 2008

Rob, thanks for addressing my concerns.

With regard to non-elevated VS it is true that Visual Studio 2008 will show a warning dialog if you do not run it as admin but you can check the box to not show the dialog again and run happily without admin.

If you choose to run with an administrator user account and with UAC disabled, that's up to you. However, for the past two years under XP and Vista, with both VS 2005 and 2008, I have not needed an administrator user account nor have I needed to elevate Visual Studio. This is while developing with ASP.NET, WinForms, WCF, and SQL. In fact, the other two devs I work with have followed suit and wouldn't go back to admin now.

In Commerce.MVC you are working with pure .NET code. You aren't trying to register any classes as COM objects. You aren't developing a Windows Service. And you are using the Visual Studio Development Server/Cassini instead of IIS. All of this works perfectly in a non-elevated VS without any need to deal with UAC prompts.

The MS documentation takes a very broad-stroke approach... run VS elevated to avoid any issues. In reality there are only a few niches that are require elevation.

Ultimately, your website will be deployed to production, running as a limited user account. Replicate that environment during development and you'll find any privilege issues much sooner in the dev cycle.

If I have been convincing enough and you do choose to try non-elevated, please let me know where you hit any pain points.

Regards,

--

Jason


Rob Conery avatar
Rob Conery says:
Monday, May 26, 2008

Thanks Jason :). I'm reading about how it works for you and (don't get me wrong) that's great ... for you. For me, I run elevated because I invariably end up telling it to elevate anyway :).

I install a lot of things that require it - I'll leave it at that.

So two things back at you:

1) I agree it's up to me :). We're all good, smart people most of the time :), capable of knowing what's good for us. I don't know if I'd qualify what I'm doing as a bad example. If you give me enough beers I might even tell you more about what I disable on my machine the minute I can :).

2) What harm comes from *not* running elevated? Can you give me a scenario (a realistic one) where clicking an "OK, elevate" button will stop me from doing something dumb?


Jason Stangroome avatar
Jason Stangroome says:
Monday, May 26, 2008

Rob,

1. If you find yourself in Australia (Tech.Ed AU maybe?) or I find myself in your country, we'll see about those beers.

2. Not sure if I understand your phrasing entirely but firstly my user account is not an Administrator so UAC expects a password (and a username too in domain environments) hence I think about what I'm doing a bit more than if it was just a "OK, elevate" button.

Secondly, inappropriate access to the file system or registry (ie requesting read-write when you only need read or saving user settings to the app folder) are the obvious, realistic problems that crop up. Admittedly, smart client is affected by this more than web sites but I know many shared hosting environments are tightly locked down.

Ultimately, while you may do very different things on a day-to-day basis, I don't believe anything you've done in the screencasts requires elevation and that '(Administrator)' text in the VS title bar really glows at me. Perhaps the next screencast could be done with a non-elevated VS just to show everyone just how cool MVC is: you don't need to develop as admin if don't want to.

Cheers,

--

Jason


BrianE avatar
BrianE says:
Tuesday, May 27, 2008

@Everyone who tells Rob he is doing it wrong

Keep it up!! :) Not that I think Rob is actually doing it wrong or making bad decisions... I'm in no position to judge...

I think the best part about this series is the interaction and comments from the public; the good, the bad, and the ugly. I've learned much more from the discussion about "which way" to implement something, than from watching a webcast or reading about it from a single source. The latter never does justice to the alternatives and so the noobs (me) get a very limited picture. And even though I know my picture is limited, I sometimes get caught up in the paradox of how do I learn something I don't know I need to learn...

The comments from everyone telling Rob he has done it wrong and the ensuing debate are great starting points for me! And though I know Rob probably gets tired of all the comments that seem like gnit picking to him (and truthfuly some of the commentors probably are =), the extra context we get from the answers will occasionally trigger an aha! moment for the noobs.

So thanks again to everyone telling Rob he is doing it wrong, but espcially to Rob for being able to withstand all of the criticism and gnit picking. This has been a great learning experience so far!


Chris Rock avatar
Chris Rock says:
Tuesday, May 27, 2008

Hi Rob. I'm sure you're busy and sorry to spam an unrelated blog post, but is there any chance you can fix the broken image links in:

blog.wekeroad.com/.../linqtosql-ranch

and

blog.wekeroad.com/.../linqtosql-momma

?

Thanks in advance,

-c


Jamie Crutchley avatar
Jamie Crutchley says:
Wednesday, May 28, 2008

Regarding using parameters on your authenticate method, try this with Preview 3:

1. Add "username" and "password" parameters to your authenticate action.

2. Add a route for your authenticate method with no defaults for username or password and add an HtmlMethodConstraint to make it POST-only. Mine looks like this:

routes.MapRoute(

"Authenticate",

"security/authenticate",

new { controller = "security", action="authenticate" },

new { httpMethod = new HttpMethodConstraint("POST") });

3. Add the form to your login page like this:

using (Html.Form<HomeController>(c => c.Authenticate("", ""))).

4. Add "username" and "password" input boxes to the form.

When I tried that, the action got written out as "/myapp/security/authenticate" and the values from the form were sent in. Makes it nice and testable without all of the mocking stuff. Granted, it might not work with a more generic route definition, but this seems cleaner and easier to test to me than reading the post variables directly.


Chance avatar
Chance says:
Wednesday, May 28, 2008

Rob:

I am having issues using your .bak file to generate the DB. Unfortunately I am attempting to do so with Express because I messed up the order of operation installing SQL Server and I can't get SQL Server to overwrite the newer edition of SQL Server Express. Debating whether or not I should risk the headache of uninstalling / reinstalling.


Rob Conery avatar
Rob Conery says:
Wednesday, May 28, 2008

@Jamie: Two issues with your suggestion:

1) I don't mind "all of the mocking stuff" - that's what it's here for :). I'd rather keep my app as simple as possible.

2) Tell me this isn't a security hole:

myapp/.../password


Stephen avatar
Stephen says:
Wednesday, May 28, 2008

Actually on the talk about vs evalation, I run vs elevated at work, its annoying because explorer cannot properly start vs when I double clicked a file.. but in terms of web development, I got frequent weirdness related around trust and debugging..

I've been running vs elevated since a few weeks after moving to vista (at launch) so you'll need to forgive me if I cant remember the specifics of the nightmares.


Erik L avatar
Erik L says:
Wednesday, May 28, 2008

Rob,

It doesn't actually put it in the URL, It populates method parameters from the form if they are not populated in the route. Personally I think it would be nice to tell the route in the defaults section that they are populated from the form so that the action link could still make a stronly typed outbound rout match. An alternative would be to somehow tell the actionlink that that the parameter is poulated fromt he form so it didn't use that parameter. It bugs me not to be able to use my lambda expression in this case. I don't really know about the username/password scenario, but it is a really nice way to use the same method for information that can be populated either from your url or postdata. Thing paged data that is initially populated from a form.

Erik


Erik L avatar
Erik L says:
Wednesday, May 28, 2008

WOW, I really need to turn on a in browser spellchecker.

Erik


Jamie Crutchley avatar
Jamie Crutchley says:
Wednesday, May 28, 2008

@Rob:

Erik's right, it doesn't put username or password in the URL. Url stays "/security/authentication" with values being sent in form post just like your solution.

@Erik:

It does let you use the Lambda form for a strongly typed URL - you just have to be a little wierd about it and use blank strings for the parameters.

Not saying this is for everybody, I just liked it better than using "this = that" to pull stuff from the form. Course this method might fall on it's face depending on how validation stuff gets implemented.


Erik avatar
Erik says:
Wednesday, May 28, 2008

It does let you use the Lambda form for a strongly typed URL - you just have to be a little wierd about it and use blank strings for the parameters.

@Jamie

Interesting, I will play with that. How does this work when the methods parameter isn't a string?


Rob Conery avatar
Rob Conery says:
Wednesday, May 28, 2008

@Erik/@Jamie: Sounds good - but what if you *did* pass them in via URL in an effort to spoof the site (for whatever reason). I see the POST constraint but I'm not convinced that Routing wouldn't try and pull it from the URL. I haven't tried - just don't know yet :). Worth checking out.

I'll give your solution here a whirl when I move to Preview 3... thanks!


Jamie Crutchley avatar
Jamie Crutchley says:
Wednesday, May 28, 2008

@Rob: The way my routes were set up, it just gave me the standard 404 when I tried to enter username/password values in the URL. You'd have to be careful though not to get a later route that matches somehow and happily sends it on as a GET.

@Erik: If you use a nullable, you can use a null in your Lambda expression and get the same behavior. I redefined as "Authentication(string username, int? pin)" and when my Lambda had "Authentication("", null)" in it, I got the url "/security/authentication" with the proper form values being passed in. Has to be nullable though, because if you use a real value in your lambda like 0 or -1, that gets sent as a querystring argument.


@Rob avatar
@Rob says:
Wednesday, May 28, 2008

@Erik/@Jamie: Sounds good - but what if you *did* pass them in via URL in an effort to spoof the site (for whatever reason). I see the POST constraint but I'm not convinced that Routing wouldn't try and pull it from the URL

@Rob,

IMO the post constraint isn't even necessary, I think that is new to preview 3. It also works with preview 2. Without a route that defines the parameters, Where is it going to pull it from? i.e. no {username} or {password} in your route and where is it going to get it from. I hope the routing engine doesn't start trying to decide what route I wanted instead of the routes I actually created. That said, Having {username}=Request.Whatever in the route defaults would be really cool.

Erik


Erik avatar
Erik says:
Wednesday, May 28, 2008

Ahh, thinking more, I gues you mean in a Querystring. I am not sure I understand how much more of a security vulnerabillity this is. You can't really use ReadFromRequest either if this is a concern. I guess you can put catchall paramater at the end of your route if you don't want a Qerystring passed in. On the other hand, Is really much harder to post data than to put it in a request from a security stand point?


flipdoubt avatar
flipdoubt says:
Thursday, May 29, 2008

I know this is going to sound basic, but I'm experiencing some Windows Media Player problems running inside Firefox. Every time I pause each video and wind the slider back just a bit to see something Rob moved away from, my video pauses permanently. The audio continues, but the video stays stuck at the point where I paused it. There are times when the video advances to a particular frame, but it never starts rolling again.

I've since given up watching the video in Firefox (my browser of choice), but this is the third time I've had to restart the video. Errgh.


herbrandson avatar
herbrandson says:
Thursday, May 29, 2008

First, thanks for this series. It’s been extremely helpful to see someone else’s thought process when architecting an application. There are lots of ideas here that I intend to steel for my next project :)

Second, I noticed something on one of the previous screen casts, but I thought my comment would be more likely to be noticed here.

In one of the database tables, you made “Username” the primary key. I believe that the idea was that you wanted the clustered index on that field since most of the querying would be by username.

You should be able to still put a clustered index on the Username field without making it the primary key. By default, MSSQL makes the primary key the clustered key, but you are able to change that. I think this could save a lot of headache the first time someone uses an inappropriate username and someone needs to go in and change it.

Thanks again for this project. I’m really enjoying it!


Rob Conery avatar
Rob Conery says:
Thursday, May 29, 2008

@flipdoubt: you're watching it *inside* FireFox? Try right-click and use Windows Media (or WMV player of your choice).

@herbrandson: Good point. I'm a big fan on integer keys but if you're not going to use them, it's just extra noise in the DB (well, that was my thinking anyway).

Interesting thought RE username being innapropriate...


Guy avatar
Guy says:
Friday, May 30, 2008

Going with TDD, best practice, consistency, and all these extra steps are we also going to converge on one style of code?

What kind of results would we get against Source Analysis 4.2 (StyleCop).

I just wondering if one day we will all agree on one style.

The two big things I see are more than like the mostly debated are:

"In fact, the differences between the 'Source Analysis style' and the 'Framework Design Guidelines style' are relatively minor. One of the biggest differences is that the framework style prefixes private and internal fields with an underscore, while Source Analysis style omits the underscore and instead prefixes all class members with this."

- Source Code Analysis blog -

and

White spacing.


Damian avatar
Damian says:
Thursday, June 19, 2008

Hi Rob,

Great series. I have watched it all over the last three days and had a go at it my self for my off licence store front.

One thing that's bugging me is the lack of tests for the SessionCartRepository and the ProfileCartRepository!

Am I missing something or have you skipped the TDD for these two classes?

I tried to have a go myself but got very stuck with mocking the Session.

Aside from that the series has got me very excited, I've even changed my VS Theme.



Search Me
Index Of MVC Screencasts

You can watch all of the MVC Screencasts up at ASP.NET, and even leave comments if you like.

Subscribe

Popular Posts
 
My Tweets
  • Isn't the Rails/Asshole thing dead? http://tinyurl.com/57dmvx
  • Pushups last night: 17, 13, 9, 7, 3
  • @kevindente my wife (and me) consider the Roomba to be on par with Tivo in terms of generation-defining technology
  • @kevindente I'll hold you and we can cry together. Maybe you can ... even ... blog about it.
  • Writing tests for InventoryService - talk about a slipper-slope process! Is there such a thing as Cart Concurrency? I dunno! Maybe?
  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).