An abstract factory testing module that takes parameters

For an abstract factory implementation:

public class FooFactory : IFooFactory { public IFoo Create(object param1, object param2) { return new Foo(param1, param2); } } 

What unit tests will be written for this class? How can I verify that param1 and param2 have been redirected to create Foo? Should I do these public Foo properties? Wouldn't that break encapsulation? Or should I leave this to test integration?

+8
c # dependency-injection abstract-factory
source share
5 answers

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
+11
source share

Well, apparently, these parameters make the returned IFoo have something true. Check if this is true for the returned instance.

+2
source share

You can make FooFactory a universal class that expects Foo as a parameter and indicates that it is a layout. Calling factory.Create (...) then creates an instance of the layout, and you could confirm that the layout received the expected arguments.

0
source share

It depends on what Foo does with 2 input objects. Check out something that Foo does with them that you can easily request. For example, if methods are called on objects (including getters or setters), you can specify mocks or stubs, which you can check if you called the expected things. If there is anything on IFoo that you can test for, you can pass the known values ​​through your mock objects for testing after creation. The objects themselves should not be publicly available to check if you have passed them.

I can also check if the expected type (Foo) is returned, depending on whether the other behavior depends on the difference between IFoo implementations.

If there are any errors that may occur, I would try to drive them into what happens if you pass zero for one or both objects, etc. Foo will be checked separately, of course, so you can assume during the FooFactory test that Foo does what it should.

0
source share

Factories, as a rule, are not tested individually, at least in my experience - there are not many of them in unit testing. . Since the implementation of this implementation interface, therefore, refers to a specific implementation. Thus, Foo mockery does not allow him to check whether he receives those parameters passed.

On the other hand, usually DI containers work like factories, so if you use them you won’t need a factory.

0
source share

All Articles