Using Reflection to Set a Private Property in C#
— software development, coding, testing, c# — 4 min read
When testing a class, you often need to assign a value to a property - a property which you wouldn't otherwise want to allow to be set. In this scenario, reflection can be a valuable tool.
Reflection? What's that?
Reflection is a tool in C# that can be used to access the metadata of a class. It has a number of uses, but it should generally be used as a last resort, as there are some drawbacks. Using reflection can lead to some performance limitations. More importantly, reflection - as you'll see below - allows you to access properties marked as private, protected, internal, etc. Theoretically, those properties were marked as such for a reason, so it's not necessarily a great idea to go and mess with that encapsulation.
However, in this case, we have a good reason: testing. Test code can bend rules that production code cannot. Below is a good use-case scenario for reflection.
The Problem
Imagine a Member
class in a gym's user management appliection. A Member
probably has some basic properties like Name
, Email
, SubscriptionPlan
, etc. Another important property might be DateJoined
.
public class Member{ public string Name {get; set;} public string Email {get; set;} public SubscriptionPlan SubscriptionPlan {get; set;} public DateTime DateJoined {get; private set;}
public Member(string name, string email, SubscriptionPlan subscriptionPlan) { Name = name; Email = email; SubscriptionPlan = subscriptionPlan; DateJoined = DateTime.Today; }
// other code here}
Above, DateJoined
has a private setter and is set to DateTime.Today
in the constructor. There is virtually no scenario in which you would want to change the value of DateJoined
. Therefore, there is no reason to give this property a public setter and it is probably a bad idea to do so.
Alright, fair enough. But what if I want to test the CheckIfMemberNeedsAnniversaryGift
method of AnniversaryGiftService
? Now things get tricky.
public class AnniversaryGiftService{ public void SendGiftIfNeeded(Member member) { if (CheckIfMemberNeedsAnniversaryGift(member)) SendAnniversaryGift(); }
public bool CheckIfMemberNeedsAnniversaryGift(Member member) { return (member.DateJoined.Day == DateTime.Today.Day && member.DateJoined.Month == DateTime.Today.Month); }
public void SendAnniversaryGift() { // some code }}
As you can see, the logic of CheckIfMemberNeedsAnniversaryGift
is entirely dependent on member
's DateJoined
. But as discussed above, DateJoined
has a private setter. So how can we test this method?
You guessed it - reflection!
The Solution
One implementation of reflection, which we will use here, involves using extension methods to set the private DateJoined
property of Member
:
public static class ReflectionHelperExtensionMethods{ // from https://stackoverflow.com/a/1565766/13680266 public static void SetPrivateDateTimePropertyValue(this Member member, string propName, DateTime newValue) { PropertyInfo propertyInfo = typeof(Member).GetProperty(propName); if (propertyInfo == null) return; propertyInfo.SetValue(member, newValue); }}
Essentially, the above code scans the metadata of the Member
class to find a property of the name passed in. In this case, we would pass "DateJoined"
, and then the SetValue
method above would set that property to newValue
, without regard for that property being marked private
.
If you're unfamiliar with extension methods, this article provides a nice overview of their use in tests.
Finally, here is the test we can write now that the reflection extension method is set up:
public static class AnniversaryGiftServiceCheckIfMemberNeedsAnniversaryGift{ // other code omitted
[Test] public void ReturnsTrueGivenMemberWithDateJoined1YearAgo { var testMember = new Member("testName", "testEmail", "testSubscriptionPlan");
// use reflection to set DateJoined property testMember.SetPrivateDateTimePropertyValue("DateJoined", DateTime.Today.AddYears(-1));
var memberNeedsAnniversaryGift = _anniversaryGiftService.CheckIfMemberNeedsAnniversaryGift(testMember);
Assert.True(memberNeedsAnniversaryGift); }}
Conclusion
This was a pretty simple use case. In fact, we could have omitted the propName
parameter of SetPrivateDateTimePropertyValue
and renamed it to SetDateJoinedPropertyValue
:
// from https://stackoverflow.com/a/1565766/13680266public static void SetDateJoinedPropertyValue(this Member member, DateTime newValue){ PropertyInfo propertyInfo = typeof(Member).GetProperty("DateJoined"); if (propertyInfo == null) return; propertyInfo.SetValue(member, newValue);}
However, this diminishes flexibility later on if Member
gets more complex and has more DateTime
properties with private setters, like a DateOfSubscriptionExpiration
, for example. The propName
parameter allows us to use the above method with more flexibility in a more complex and less contrived use case.
Resources
The StackOverflow thread that explains how to use reflection to set a private property, as we did above.
A C# Corner article explaining reflection and its uses in detail.
Thanks for reading! I hope you find this and other articles here at ilyanaDev helpful! Be sure to follow me on Twitter @ilyanaDev.