Reputation: 3063
I have a controller that overrides OnActionExecuting
and does something like this:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
string tenantDomain = filterContext.RouteData.Values["tenantDomain"] as string;
if (!string.IsNullOrWhiteSpace(tenantDomain))
{
using (var tx = BeginTransaction())
{
this.Tenant = repo.FindOne(t => t.Domain == tenantDomain);
}
}
}
Tenant
is a protected property with a private setter. The class itself is an abstract base controller that my real controllers derive from. I have code in other controllers that looks a lot like this:
if (Tenant == null)
{
// Do something
}
else
{
// Do something else
}
How do I test this code? What I need to do is to somehow set the Tenant property, but I can't because:
Changing the visibility of Tenant
doesn't "feel" right. What are my alternatives to unit test my derived controllers?
Upvotes: 5
Views: 523
Reputation: 5010
There are two ways to accomplish this
Mock the Tenant Property (Standard Way)
Mock<TenantType> tenantMock = new Mock<TenantType>();
var controller = new TargetController();
Controller.Tenant = tenantMock.Object;
Use reflection to set the private property (for those who are not familiar with mocking frameworks)
E.g.,
private static void SetPrivateSetPropertyValue(Type type, object obj, string fieldName, object value)
{
PropertyInfo propInfo = type.GetProperty(fieldName);
propInfo.SetValue(obj, value, null);
}
Consuming Code
this.SetPrivateSetPropertyValue(TenantType.GetType(),controller , "Tenant",tenantInstance);
Hope this helps,
Thanks , Vijay.
Upvotes: 0
Reputation: 4313
Ok after re-reading @driis answer I think there is a more explicit way to do this but it will require using DI in your controller factory. (There are many samples of how to do this online.)
First we are going to encapsulate the logic of how to get a Tenant from a route parameter.
public class TenantFinder : ITenantFinder
{
// todo DI for repo
public Tenant Find(string tenantDomain)
{
if (string.IsNullOrWhiteSpace(tenantDomain))
{
return null;
}
using (var tx = BeginTransaction())
{
return repo.FindOne(t => t.Domain == tenantDomain);
}
}
}
Then we can inject this class into the controller. (Icky part is putting the constructor parameter everywhere!) So in the abstract controller:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
string tenantDomain = filterContext.RouteData.Values["tenantDomain"] as string;
Tenant = Finder.Find(tenantDomain );
}
Then in your unit test you can create a stub of ITenantFinder
to inject the tenant you want to use. You could even make a fake class like NullTenantFinder
to handle the null cases.
Upvotes: 0
Reputation: 308
I understand that you want to test the concrete controllers that inherits from your base class.
It looks like you want want to stub your repo to return the tenant corresponding to the context of your test.
Your need:
What I need to do is to somehow set the Tenant property
And from your implementation the tenant is provide by the repo
this.Tenant = repo.FindOne(t => t.Domain == tenantDomain);
What you need is to be able to inject your repo by some way and from the name "repo" that sounds like an ok thing.
does that make sense?
-- Dom
Upvotes: 0
Reputation: 164281
Here's how I would do it:
Create a concrete class in your test project, that inherits from your abstract controller, and exposes Tenant as a public property. There is nothing wrong with having mock or dummy code in the test project (and of course, dummy code that just facilitates testing, should never be in the production projects).
If that is not a good fit for your situation; you could use Visual Studio's Private Accessor Assembly feature to generate a type where you can access your Tenant property. It is available in the IDE, or in command-line form.
Upvotes: 1
Reputation: 4313
You could create a method on the abstract base for use only in unit testing. You can either make it public or internal and set [assembly: InternalsVisibleTo("Tests")]
on the MVC project like this.
internal void SetTenant(Tenant testValue)
{
Tenant = testValue;
}
Upvotes: 0
Reputation: 3686
Using reflection?
Something like this should work (not tested):
YourController.GetType()
.GetProperty("Tenant", BindingFlags.NonPublic | BindingFlags.Instance)
.SetValue(yourControllerInstance, null, theTestValue);
Upvotes: 0