Don't like visibility levels, change that

This came up with respect to MS MVC. The MS MVC team has decided to make some methods protected, and that makes testing using Rhino Mocks a bit of a pain.

Let us assume that I want test that the call to ProductController.Index() will render the "index.aspx" view. I can do something like:

[Test]
public void ShouldAskToRenderTheIndexAspxView()
{
	MockRepository mocks = new MockRepository();
	ProductController controller = mocks.PartialMock<ProductController>();
	using(mocks.Record())
	{
		controller.RenderView("index.aspx"); // <<-- Compiler error here, RenderView is protected!
	}

	using (mocks.Playback())
	{
		controller.Index();
	}
}

Problem, part of the Rhino Mocks design goal was the use of the compiler and tools as verifiers. I explicitly do not want to have strings in my code, and I put them only where I have no other choice. However, since Rhino Mocks by runtime subclassing, there is no issue with reflection calls...

This means that we can get around this limitation by using the following extension method:

public static void RenderView(this Controller self, string action)
{
	typeof(Controller).GetMethod("RenderView").Invoke(self, new object[] { action} );
}

This is it, it will compile and work now.

Print | posted on Thursday, December 13, 2007 10:16 AM

Feedback


Gravatar

# re: Don't like visibility levels, change that 12/13/2007 11:29 AM Haacked

Nice! I need to update my post with this technique.


Gravatar

# re: Don't like visibility levels, change that 12/13/2007 12:39 PM Thomas Gravgaard

Just one slight change is needed to the code. The RenderView extension method has to look like this since the original method is protected. But is is still a very neat technique. Seems like MVC is drawing out a lot of the cool stuff you can do with extension methods:

public static void RenderView(this Controller self, string action)
{
typeof(Controller).GetMethod("RenderView",
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic,
null,
new Type[] { typeof(String) },
null
).Invoke(self, new object[] { action });
}


Gravatar

# re: Don't like visibility levels, change that 12/13/2007 12:45 PM Grimace of Despair

Could someone hint the guys at Microsoft that their code "protection" through internal/private/protected members all over the place might just not be such a good idea in the light of extensibility?


Gravatar

# re: Don't like visibility levels, change that 12/13/2007 3:23 PM John Chapman

This may sound like blasphemy, but why doesn't Rhino mocks just add the ability to reference a method by name instead of having to call the method when registering it in the framework in order to handle non-public members.

I love the way it works for public methods, but if I need to add an extension method and then use reflection to wire up my mock I may as well be using the framework instead since I'll have the weakly typed issues anyway.

I would just feel dirty using extension methods in this way. Plus it seems like extra unnecessary steps every time you need to do something like this.


Gravatar

# re: Don't like visibility levels, change that 12/13/2007 3:31 PM Ayende Rahien

John,
I am accepting patches :-)


Gravatar

# re: Don't like visibility levels, change that 12/13/2007 4:02 PM Håkan Forss

I'm a little into John's approach but with a twist.
Wouldn't it be possible to have Rhino mock expose all protected methods as public on creation of a partial mock



Gravatar

# re: Don't like visibility levels, change that 12/13/2007 4:04 PM Ayende Rahien

Certainly,
But you won't get the compile time access to that, RhinoMocks works at run time.


Gravatar

# re: Don't like visibility levels, change that 12/13/2007 4:49 PM Jimmy Bogard

Just curious, would you recommend this mechanism over something like "Subclass and override"? For example:

public class TestController : Controller
{
public new virtual void RenderView(string action)
{
base.RenderView(action);
}
}

Slightly more annoying, but with compile-time safety.


Gravatar

# re: Don't like visibility levels, change that 12/27/2007 2:00 PM Vijay Santhanam

Haack has an interesting post on this topic:
http://haacked.com/archive/2007/12/09/writing-unit-tests-for-controller-actions.aspx

He recommends the Test Specific Subclass if you have protected methods. I think he implies in his post that most protected virtual methods will become public to assist TDD - which would ideally solve this problem.

Comments have been closed on this topic.