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