Automocking and the Dependency Inversion Principle
I had reason to revisit the automocked base class from a previous blog post. I am working with another code base and have new opportunities for automocking. We have a lot of internal classes. Approximately 30% of the classes are marked as internal. The old approach did not work anymore.
With an internal subject, I got this error:
Inconsistent accessibility: base class 'WithSubject<HelloWorld>' is less accessible than class 'When_GetMessage'
The Dependency Inversion Principle
The D in SOLID stands for the Dependency Inversion Principle:
A. High-level modules should not depend on low-level modules. Both should depend on abstractions. B. Abstractions should not depend on details. Details should depend on abstractions.
In other words:
Depend on abstractions, not on concretions.
One way to enforce this between projects in C# is to make classes internal
.
The internal access modifier
The internal
access modifier:
Internal types or members are accessible only within files in the same assembly
This is useful:
A common use of internal access is in component-based development because it enables a group of components to cooperate in a private manner without being exposed to the rest of the application code.
How can the unit tests access the internal classes then?
The [InternalsVisibleTo]
attribute:
types that are ordinarily visible only within the current assembly are visible to a specified assembly.
We will add the attribute to the AssemblyInfo file in the project under test, to make the unit test project a friend assembly.
We will use an IoC container to configure the creation of the internal classes. The clients will depend on public interfaces and the IoC container.
WithFakes
My favorite framework for testing is still Machine.Specifications in combination with Machine.Fakes for automocking support.
At work, we use:
I will mimic the Machine.Fakes WithFakes
base class:
-
The test fixture will inherit from
WithFakes
-
Use
The<TFake>()
method for creating fakes
My implementation will use:
Code
You can get the example code at https://github.com/hlaueriksson/ConductOfCode
WithFakes
The Subject<TSubject>()
method gives access the class under test.
This is how the error Inconsistent accessibility: base class 'WithSubject<HelloWorld>' is less accessible than class 'When_GetMessage'
is solved.
The The<TFake>()
method gives access to the injected dependencies from the subject.
The With<TFake>()
methods can be used to inject real or fake objects into the subject.
The subject
The interfaces are public
, the concrete classes are internal
.
The creation of internal classes is configured with StructureMap, the IoC container we are using.
The AssemblyInfo.cs
is also modified to make the subject accessible for the unit tests:
[assembly: InternalsVisibleTo("ConductOfCode.Tests")]
.
The client
The client depends on interfaces and uses the IoC container to create concrete classes.
The IoC container scans the assemblies for registries with configuration.
The tests
The Subject
property gives access to the automocked instance via the Subject<TSubject>()
method from the base class.
The With<TFake>()
methods can be used to inject and setup mocks.
The The<TFake>()
method is used for setup and verification of mocks.
The unit test sessions