Here is how I would write one of several unit tests for such a factory (with xUnit.net ):
[Fact] public void CreateReturnsInstanceWithCorrectParam1() { var sut = new FooFactory(); var expected = new object(); var actual = sut.Create(expected, new object()); var concrete = Assert.IsAssignableFrom<Foo>(actual); Assert.Equal(expected, concrete.Object1); }
Does it break encapsulation? Yes and no ... a bit. Encapsulation is not only about hiding data - more importantly, about protecting invariants of objects .
Suppose Foo provides this open API:
public class Foo : IFoo { public Foo(object param1, object param2); public void MethodDefinedByInterface(); public object Object1 { get; } }
While the Object1 property slightly violates the Demeter Law , it does not fit into the class invariants because it is read-only.
In addition, the Object1 property is part of a specific Foo class, not the IFoo interface:
public interface IFoo { void MethodDefinedByInterface(); }
Once you understand that in a loosely coupled API, specific elements are implementation details , such specific read-only properties have very little effect on encapsulation. Think of it this way:
The public Foo constructor is also part of the API of a specific Foo class, so just by checking the public API, we find out that param1 and param2 are part of the class. In a sense, this already “breaks down encapsulation,” so each parameter that is available as read-only properties in a particular class does not change much.
Such properties give the advantage that we can now unit test the structural form of the Foo class returned by factory.
This is much simpler than repeating a set of behavioral unit tests, which we assume already cover a particular Foo class. This almost looks like a logical proof:
- From the tests of a specific Foo class, we know that it correctly uses / interacts with its constructor parameters.
- From these tests, we also know that the constructor parameter is displayed as a read-only property.
- From the FooFactory test, we know that it returns an instance of a specific Foo class.
- In addition, from the tests of the Create method, we know that its parameters are correctly passed to the Foo constructor.
- QED