The TestControllerBuilder in MVCContrib’s TestHelper library has a hard dependency on Rhino Mocks. I’m not a big fan of Rhino Mocks, I prefer to use Moq, and while it’s not a big deal to reference two separate mocking libraries, I’d rather just have one mocking library in my project. I’m working on a patch to address this open workitem in MVCContrib. I don’t want to create a hard reference to Moq, so that means I have to access Moq, and all it’s generic types and methods, using reflection. This post presents my Moq proxy class, based on a similar idea from StructureMap’s auto-mocking container.
The TestControllerBuilder in MVCContrib.TestHelper is a handy class for attaching mocks to an MVC Controller class to facilitate testing. Unfortunately, it uses Rhino Mocks, which means you must include Rhino Mocks in your projects in order to leverage it. My test projects already reference Moq (my preferred testing framework), and I don’t like the idea of carrying around two libraries that provide the same capabilities. I’m not the only one, as there has been an open work item to address this issue for quite some time now. One of the nice things about Open-Source Software is that instead of wasting energy complaining about something, I can try to fix it.
I have three main goals for the fix. First, it should not be a breaking change. Existing consumers of the API should be unaffected. Second, it should not create a hard reference between MVCContrib and Moq. MVCContrib is already using Rhino Mocks for its own testing, and I’m trying to remove the need for referencing two mocking frameworks in my own projects, so it would be a bit ironic if I required MVCContrib to pick up a dependency on Moq. Finally, I want it to be very simple to swap out the mocking library; in fact, I’d like it to automatically use whichever library is available. I’d even like it to be pluggable so that a different mocking library could be plugged in if desired.
I’m not 100% tied to this approach yet (I’m still thinking of this more as a spike for now), but I’m using the strategy pattern to decouple TestControllerBuilder from the mocking library. There are other places in MVCContrib’s TestHelper library that are doing mocking, so I will probably change my approach, but this worked well as a proof-of-concept. TestControllerBuilder now accepts an IControllerBuilderStrategy that handles the actual mocking and any other library-specific setup. The interface is quite simple:
public interface IControllerBuilderStrategy { void Setup(TestControllerBuilder testControllerBuilder); }
By default, TestControllerBuilder uses the RhinoMocksControllerBuilder (this satisfies my first goal: existing consumers of the API will get the exact same behavior as before). Because MVCContrib.TestHelper has a direct dependency on Rhino Mocks, RhinoMocksControllerBuilder is a straight-forward refactoring of TestControllerBuilder’s mock-dependent logic into a new class:
public class RhinoMocksControllerBuilder : IControllerBuilderStrategy { protected MockRepository _mocks = new MockRepository(); public void Setup(TestControllerBuilder testControllerBuilder) { var httpContext = _mocks.DynamicMock<HttpContextBase>(); var request = _mocks.DynamicMock<HttpRequestBase>(); var response = _mocks.DynamicMock<HttpResponseBase>(); var server = _mocks.DynamicMock<HttpServerUtilityBase>(); var cache = HttpRuntime.Cache; SetupResult.For(httpContext.Request).Return(request); SetupResult.For(httpContext.Response).Return(response); SetupResult.For(httpContext.Session).Return(testControllerBuilder.Session); SetupResult.For(httpContext.Server).Return(server); SetupResult.For(httpContext.Cache).Return(cache); SetupResult.For(request.QueryString).Return(testControllerBuilder.QueryString); SetupResult.For(request.Form).Return(testControllerBuilder.Form); SetupResult.For(request.AcceptTypes).Do((Func<string[]>)(() => testControllerBuilder.AcceptTypes)); SetupResult.For(request.Files).Return((HttpFileCollectionBase)testControllerBuilder.Files); Func<NameValueCollection> paramsFunc = () => new NameValueCollection { testControllerBuilder.QueryString, testControllerBuilder.Form }; SetupResult.For(request.Params).Do(paramsFunc); SetupResult.For(request.AppRelativeCurrentExecutionFilePath).Do( (Func<string>)(() => testControllerBuilder.AppRelativeCurrentExecutionFilePath)); SetupResult.For(request.ApplicationPath).Do((Func<string>)(() => testControllerBuilder.ApplicationPath)); SetupResult.For(request.PathInfo).Do((Func<string>)(() => testControllerBuilder.PathInfo)); SetupResult.For(request.RawUrl).Do((Func<string>)(() => testControllerBuilder.RawUrl)); SetupResult.For(response.Status).PropertyBehavior(); SetupResult.For(httpContext.User).PropertyBehavior(); _mocks.Replay(httpContext); _mocks.Replay(request); _mocks.Replay(response); testControllerBuilder.HttpContext = httpContext; } }
Things get a little trickier for the Moq version. Since TestHelper can’t reference Moq directly, it can’t use the API directly. I decided to use the proxy pattern after reviewing how StructureMap’s AutoMocking Container handles a similar situation. The builder itself is still very straight forward. Though the proxy’s methods don’t map one-to-one to the underlying Moq API, they are pretty close:
public class MoqControllerBuilder : IControllerBuilderStrategy { private static Exception _loadException; private static MoqProxy _proxy; static MoqControllerBuilder() { try { _proxy = new MoqProxy(); } catch (Exception ex) { _loadException = ex; } } public void Setup(TestControllerBuilder testControllerBuilder) { if (_proxy == null) { throw new InvalidOperationException("Cannot use MoqControllerBuilder because an error occured while loading Moq.", _loadException); } var httpContext = _proxy.DynamicMock<HttpContextBase>(); var request = _proxy.DynamicMock<HttpRequestBase>(); var response = _proxy.DynamicMock<HttpResponseBase>(); var server = _proxy.DynamicMock<HttpServerUtilityBase>(); var cache = HttpRuntime.Cache; httpContext.ReturnFor(c => c.Request, request.Object); httpContext.ReturnFor(c => c.Response, response.Object); httpContext.ReturnFor(c => c.Session, testControllerBuilder.Session); httpContext.ReturnFor(c => c.Server, server.Object); httpContext.ReturnFor(c => c.Cache, cache); httpContext.SetupProperty(c => c.User); request.ReturnFor(r => r.QueryString, testControllerBuilder.QueryString); request.ReturnFor(r => r.Form, testControllerBuilder.Form); request.ReturnFor(r => r.Files, (HttpFileCollectionBase)testControllerBuilder.Files); request.CallbackFor(r => r.AcceptTypes, () => testControllerBuilder.AcceptTypes); request.CallbackFor(r => r.Params, () => new NameValueCollection { testControllerBuilder.QueryString, testControllerBuilder.Form }); request.CallbackFor(r => r.AppRelativeCurrentExecutionFilePath, () => testControllerBuilder.AppRelativeCurrentExecutionFilePath); request.CallbackFor(r => r.ApplicationPath, () => testControllerBuilder.ApplicationPath); request.CallbackFor(r => r.PathInfo, () => testControllerBuilder.PathInfo); request.CallbackFor(r => r.RawUrl, () => testControllerBuilder.RawUrl); response.SetupProperty(r => r.Status); testControllerBuilder.HttpContext = httpContext.Object; } }
The *real* fun happens in the proxy class. First is the MoqProxy class, which handles dynamically loading the Moq API and, using reflection, creating mock objects:
public class MoqProxy { private Type _mockOpenType; public MoqProxy() { Assembly Moq = Assembly.Load("Moq"); _mockOpenType = Moq.GetType("Moq.Mock`1"); if (_mockOpenType == null) { throw new InvalidOperationException("Unable to find Type Moq.Mock<T> in assembly " + Moq.Location); } } public MockProxy<T> DynamicMock<T>() { return new MockProxy<T>(_mockOpenType.MakeGenericType(typeof(T))); } }
The MockProxy<T> class is the proxy for the underlying Mock<T> type, which is the class used for setting up expectations. This was a tricky class to implement due to the heavy use of generic parameters, but in the end, I managed to get it working:
public class MockProxy<T> { private readonly Type _mockType; private PropertyInfo _objectProperty; private object _instance; public T Object { get { return (T)_objectProperty.GetValue(_instance, null); } } public MockProxy(Type mockType) { _mockType = mockType; _instance = Activator.CreateInstance(_mockType); _objectProperty = mockType.GetProperty("Object", _mockType); } private MethodInfo GetSetupMethod<TResult>() { var openSetupMethod = _mockType.GetMethods().First(m => m.IsGenericMethod && m.Name == "Setup"); return openSetupMethod.MakeGenericMethod(typeof(TResult)); } public void ReturnFor<TResult>(Expression<Func<T, TResult>> expression, TResult result) { var setupMethod = GetSetupMethod<TResult>(); var setup = setupMethod.Invoke(_instance, new object[] { expression }); var returnsMethod = setup.GetType().GetMethod("Returns", new [] {typeof(TResult)}); returnsMethod.Invoke(setup, new object[] { result}); } public void CallbackFor<TResult>(Expression<Func<T, TResult>> expression, Func<TResult> callback) { var setupMethod = GetSetupMethod<TResult>(); var setup = setupMethod.Invoke(_instance, new object[] { expression }); var returnsMethod = setup.GetType().GetMethod("Returns", new[] { typeof(Func<TResult>) }); returnsMethod.Invoke(setup, new object[] {callback}); } public void SetupProperty<TProperty>(Expression<Func<T, TProperty>> expression) { var openSetupMethod = _mockType.GetMethods().First(m => m.Name == "SetupProperty" && m.GetParameters().Length == 1); var setupMethod = openSetupMethod.MakeGenericMethod(typeof(TProperty)); setupMethod.Invoke(_instance, new object[] {expression}); } }
Reflection is somewhat limited when it comes to working with overloaded generic methods. I ended up using this approach for finding the desired overload via LINQ.
With all the pieces in place, the TestControllerBuilder unit tests now pass when using either Rhino Mocks or Moq. Again, this isn’t my final solution, I’m still waiting on feedback on a few possible approaches to decide what’s going to actually be implemented, but the MoqProxy is probably going to be a key piece of whatever solution I move forward with.
I am in the business connected with saving consumers money and energy by using window film, and I’m continuously looking for even more information and facts to assist help make me a little better. Have you ever been in someones home that had damage due to the sun? All that damagehappens from the suns UV rays coming into the home. This is the main reason for using a home window tint film, some people only want window film for the protection from the sun and those homeowners can use our films that are nearly clear. Today we need a number of ways just to save.
I like how a world event screws up air travel globally and instantly everybody seems to have something to express about the event! <p>Given, this was a significant event, but the mode in which waves of interest pass throughout society, increase, spread, and subside is simply fascinating to observe. <p>I LOVE IT! Fantastic post.
It does seem that everybody is into this kind of stuff lately. Don’t really understand it though, but thanks for trying to explain it. Appreciate you shedding light into this matter. Keep it up
Can I get "regular" KQED if I subscribe to Satellite TV? KQED TV-9 is now available on DirecTV and DishNetwork in the Bay Area market. Under legislation passed in November 1999 by Congress and mandated by the Federal Communications Commission Must-Carry regulations, direct satellite providers must now carry local public television in those communities where they offer local packages. http://www.4pcsatellitetv.com.
I am in the business of saving families cash plus energy by making use of window film, and I am consistently trying to find supplemental knowledge to help help to make me a little bit better. Like, in today’s new construction architects like a window friendly environment and due to this fact many homeowners are faced with the problem of fading furniture, carpeting and even wood floors and walls|Architects are using so much more glass than years past but for the homeowner this brings difficulties with a rise in the summers heat, which in turn causes air conditioning costs to rise, also problems for the homes interior from the suns UV rays, had to replaced items in your home because of the fading? In these days many have to learn tactics to be able to conserve! Have high hopes this can help quite a few of your current readers.
Can anyone help me to a good web resource for doing my own privacy fence? I tried fencingguides.com but the designs there didn’t assist me as much as I would have liked.
I can see that you are are genuinely passionate about this! I am trying to build my own website and you’ve helped me with some great information.
This is my first visit here, but I will be back soon, because I really like the way you are writing, it is so simple and honest
Keep up the good writing
Well if others in your network are attending some of the other sessions you may be able to quickly find the best session for you to attend.
It must be useful, informative and non threatening.
Interesting take on this issue. I for one have seen many twists on this and can often spot the holes in the arguement however, on this occasion I belelive your writing is such that everyone should be in agreement with this. Thank you for sharing it with us.
Good read, you definitely made me consider. I work in and close to this industry, you have some excellent points.
There is a good tone to this blog post even if I may not agree with everything. Good job.
Thanks for the share, I have been readin online all day and this was actully worth reading. Thanks
Well this is very interesting indeed. Would love to read a little more of this. Great post. Thanks for the heads-up…This blog was very informative and knowledgeable.
I’ve never been on a Quad before, what should I do? We’d recommend trying an automatic on the Arrive and Drive track first to get a feel for how quad bikes handle before booking the Trail. Trail riding is all about gears (The bikes have 5 gears but no clutch so are pretty easy to ride) and is very much based on ability. Arrive and drive is suitable for everyone from age 6+ (including adults) and you are free to go round the tracks at your own pace. Trail riding is suited to people aged 16+ and you go round with Instructors. We have the largest Arrive and Drive tracks around and probably the best Trail in the UK. ATVs For Sale: http://www.atv-for-sale.com.
I found this is an informative and interesting post so i think so it is very useful and knowledgeable. I would like to thank you for the efforts you have made in writing this article. I am hoping the same best work from you in the future as well. In fact your creative writing ability has inspired me. Really the article is spreading its wings rapidly.
Thank you for your kind words Welcome First time Try-Catch-FAIL – Using Moq Dynamically skipped here on your site, founde on Google. I just wanted to thank you for taking the time to reply to my problem in such a prompt manner. You both really opened my eyes. I have read what you wrote over and over and it all makes so much sense. I appreciate it from the bottom of my heart!vaeva Send me news to [email protected]
(Salut | Bienvenue | Salut HI) Try-Catch-FAIL – Using Moq Dynamically premičre fois (sauté | borné | houblonné |) sauté ici sur votre site, Founde sur (Google | Yahoo | Bing | ASK). C’est ma premičre impression avant de commenter le contenu. Je suis ravie de trouver votre site en ligne magnifique. Je cherche manusl utilisateur (| manuels en PDF | manuel du propriétaire). J’ai trouvé magasin avec manusl utilisateur (| manuels en PDF | manuel du propriétaire) par regarder gratuitement manusl (utilisateur | manuels en PDF | manuel du propriétaire). Avez-vous actuellement un site Web gratuit avec PDF? kesa Envoyez-moi des nouvelles de [email protected]
Have you ever considered adding more videos to your blog posts to keep the readers more entertained? I mean I just read through the entire article of yours and it was quite good but since I’m more of a visual learner,I found that to be more helpful well let me know how it turns out! I love what you guys are always up too. Such clever work and reporting! Keep up the great works guys I’ve added you guys to my blogroll. This is a great article thanks for sharing this informative information.. I will visit your blog regularly for some latest post.
Who is the founder of Diecast Authority? A model car hobbyist just like you and me, he grew up liking into collecting model cars especially the Diecast model and decided to seek other hobbyists to help create a website of its kind. They want to provide a platform so other model car hobbyists can share information and knowledge about their collection. http://118diecast.org.
Do you know where i can find free webkinz codes
Buena lectura, estaré de vuelta de verificación para obtener información adicional. Hola (| Bienvenida | Hi HI) (saltó por primera vez | delimitadas | saltó | omitido) aquí en su sitio, founde el (Google | Yahoo | Bing | ASK). g|ras Envíeme noticias a [email protected]
Welcome hope you have a good day! I think I have more useful points on using Twitter to market your blog Try-Catch-FAIL – Using Moq Dynamically. The speedy black fox jumped over the tired dog. Do you now any free website with user manusl’s? preset Send me news to [email protected]
Hey, can I use some of the info found in this entry if I provide a link back to your blog?
Great Post. Keep up the good work 😉
I personally have embraced the new technologies and the CMS platforms, I think the new tools only make the web designs better. I am glad that new technologies are coming out in web design that make things easier, improved, and better looking for design.
Hey just posting to say your page is not working correct for me. i’m using opera it might be on my end though as i’ve seen other pages that are looking mucked up to me too.
Dude, that’s great! I’ll be back for you new posts!
Some specifications for mechanical, physical and optical characteristics of DVD optical discs could be downloaded as freely accessible standards from the ISO website.
Thoughtful blogpost in an area I wouldn’t have through about today without having coming across your blog.
my God, i thought you were going to chip in with some decisive insght at the end there, not leave it with "we leave it to you to decide".
Auto insurance is such a ripoff. They charge you an overblown amount, and then when it’s time to get your car fixed after an accident, the insurance company is nowhere to be found.
My organization is in the business associated with saving customers dollars and even energy by means of window film, and I am consistently on the lookout for supplemental strategies to assist make me a bit better. Have you ever noticed how your furniture fades in the sun? All that damagehappens from the suns UV rays coming into the home. That is the biggest reason people use Home Window Tinting Film, for some it is all they want window film for and those people can use our line of films that are virtually clear. Today we need a number of ways to conserve.
In May 1997, the DVD Consortium was replaced through the DVD Forum, which can be open to all other companies.
For some reason when I tried to bookmark this website, it wouldn’t allow me to save it. I know this is off subject but I am using Chrome and was wondering if the writer might know why this is so. It only occurs on this blog.
Apple’s integration of the capability to play digital movies is only one of many reasons why this wonderful piece of hardware is creating such a huge interest. In many places, there may well not be enough iPhones available to meet the demand.
I wouldnt agree all the way, but all in all great stuff.
Wow! This post is really great because it’s full of information about computer science. This is a very helpful blog I found for my project study in school. Thanks again.
Well Guys, Thanks for taking the time to discuss this, I feel strongly about it and love learning more on this topic. If possible, as you gain expertise, would you mind updating your blog with more information? It is extremely helpful for me.
Good day how is it going, I found your site after some time browsing and the truth is. the impressive quality of this post surprised me, let me tell you your website does have some compatibility issues with internet explorer, which I found highly disgusting, extremely annoying, dreadful. Looking to hear from you soon.
Hey just posting to say your site is not working correct for me. i’m using ie7 it could be me though as i’ve seen other pages that appear mucked up to me too.