Summary
Is there harm in exposing concrete class data as get-only public properties solely for the purposes of writing better unit tests?
Background
In general, I prefer to keep as much, if not all, of my data as private readonly. This ensures consumers aren't bound to the data held but rather to the behaviors a type provides. One (potential) issue I've noticed recently is writing good unit tests for factory types that implement an interface. Consider the following illustrative example:
public interface IComponent
{
void DoStuff();
}
public interface IComponentDependency
{
void HelpInDoingStuff(int data);
}
public interface IFactory
{
IComponent CreateComponent(IComponentDependency dependency)
}
public class SpecialComponent : IComponent
{
private readonly int someOtherDependency;
private readonly IComponentDependency dependency;
public SpecialComponent(IComponentDependency dependency, int someOtherDependency)
{
this.dependency = dependency;
this.someOtherDependency = someOtherDependency;
}
public void DoStuff()
{
dependency.HelpInDoingStuff(someOtherDependency);
}
}
public class SpecialComponentFactory : IFactory
{
private readonly int someOtherDependency;
public SpecialComponentFactory(int someOtherDependency)
{
this.someOtherDependency = someOtherDependency;
}
public IComponent CreateComponent(IComponentDependency dependency)
{
return new SpecialComponent(dependency, this.someOtherDependency);
}
}
When writing tests for SpecialComponentFactory the only thing available publicly to test is that SpecialComponentFactory returns an instance of SpecialComponent. What if I'd also like to confirm that SpecialComponent was indeed constructed with someOtherDependency, or in fact, that he was constructed with the specified dependency. To do this without reflection hacks we could make them protected and subclass SpecialComponent and then expose it publicly via a test fake:
public class SpecialComponent
{
protected readonly int someOtherDependency;
protected readonly IComponentDependency dependency;
public SpecialComponent(IComponentDependency dependency, int someOtherDependency)
{
this.dependency = dependency;
this.someOtherDependency = someOtherDependency;
}
public void DoStuff()
{
dependency.HelpInDoingStuff(someOtherDependency);
}
}
public class FakeSpecialComponent : SpecialComponent
{
public FakeSpecialComponent(IComponentDependency dependency, int someOtherDependency)
: base(dependency, someOtherDependency)
{
this.dependency = dependency;
this.someOtherDependency = someOtherDependency;
}
}
But this doesn't seem ideal to me. Another option is to simply expose the dependencies as public. While this would permit the test to assert that the dependencies used are correct, it also goes against my long held belief in keeping data as private as possible and only exposing the behavior.
public class SpecialComponent
{
public int SomeOtherDependency;
public IComponentDependency Sependency;
public SpecialComponent(IComponentDependency dependency, int someOtherDependency)
{
Sependency = dependency;
SomeOtherDependency = someOtherDependency;
}
public void DoStuff()
{
Dependency.HelpInDoingStuff(SomeOtherDependency);
}
}
Concluding Thoughts
If the entire solution never actually references SpecialComponent directly, but instead always references IComponent, then is there any harm (or smelliness) in the second option of exposing the dependencies as public get-only properties. But now I'm butting up against another long held belief in modifying the accessibility of members for the sole purpose of unit testing.
Question
Is there any harm in actually making the private readonly fields into public get-only properties on my concrete types? I'm trying to rationalize it as being OK since consumers of SpecialComponent only reference IComponent, so they won't have direct access to the data, but the tests could simply cast to SpecialComponent and perform the desired assert statements.
Am I over thinking all of this? :)
EDIT: I've updated the above code samples to include method declarations and implementations for IComponent to 'fill out' the example. As noted in the below comment, I could unit test that the IComponent instance returned from SpecialComponentFactory.CreateComponent behaves a certain way which would provide coverage in proving that the required dependencies were used, but this to me sounds like the least acceptable option since the unit tests for SpecialComponentFactory would unnecessarily be testing SpecialComponent's implementation, thereby giving it reasons to fail outside of it's own implementation and coupling the SpecialComponentFactory tests to the behavior of SpecialComponent