This post is prompted by a couple of things: (1) a limitation in the Moq mocking framework, (2) a look back at a unit test I wrote nearly 3 years ago when I first arrived at my current company. While you can use Moq to create an instance of a concrete class, you can’t set expectations on class members that aren’t virtual. In the case of one of our domain entities, this made it impossible to implement automated tests one of our business rules–at least not without creating real versions of multiple dependencies (and thereby creating an integration test). Or so I (incorrectly) thought.
Our solution architect sent me an unit test example that used reflection to set the non-virtual properties in question so they could be used for testing. While the approach is a bit clunky when compared to the capabilities provided by Moq, it works. Here’s some pseudo-code of an XUnit test that follows his model by using reflection to set a non-virtual property:
[Fact] public override void RuleIsTriggered() { var sde = new SomeDomainEntity(ClientId, null ); SetWorkflowStatus(sde, SomeDomainEntityStatus.PendingFirstReview); var context = GetBusinessRuleContext(sde); Assert.True(RuleUnderTest.When(context.Object)); } private void SetWorkflowStatus(SomeDomainEntity someDomainEntity, WorkflowStatus workflowStatus) { var workflowStatusProperty = typeof(SomeDomainEntity).GetProperty("WorkflowStatus"); workflowStatusProperty.SetValue(someDomainEntity, workflowStatus, null); }
With the code above, if the business rule returned by RuleUnderTest looks at WorkflowStatus to determine whether or not the instance of SomeDomainEntity is valid, the value set via reflection will be what is returned. As an aside, the “context” returned from GetBusinessRuleContext is a mock configured to return sde if the business rule looks for it as part of its execution.
After seeing the previous unit test example (and a failing unit test on another branch of code), I was reminded of a unit test I wrote back in 2012 when I was getting up to speed with a new system. Based on the information I was given at the time, our value objects all needed to implement the IEquatable interface. Since we identified value objects with IValueObject (which was just a marker interface), using reflection and a bit of LINQ resulted in a test that would fail if any types implementing IValueObject did not also implement IEquatable. The test class is available here. If you need similar functionality for your own purposes, changing the types reflected on is quite a simple matter.