Automocking fields using NUnit
From time to time I get to teach and mentor Java developers on the fine art of unit testing. There are many similarities when unit testing in Java and .NET but more interesting are the differences between the two.
Faking objects in Java using Mockito
One of the well-used Java Isolation (Mocking) frameworks is called Mockito which is easy to use, and has an API that reminds me of FakeItEasy, Isolator or Moq (as well as others). Creating a new fake object using Mockito is as simple as writing mock(SomeDependecy.class).
Mockito also enables automatically creating fakes, spies (partial fakes) and classes with dependencies using annotations (similar to attributes in Java):
public class ArticleManagerTest extends SampleBaseTestCase {
@Mock
private ArticleCalculator calculator;
@Mock(name = "database")
private ArticleDatabase dbMock; // note the mock name attribute
@Spy
private UserProvider userProvider = new ConsumerUserProvider();
@InjectMocks
private ArticleManager manager;
@Test
public void shouldDoSomething() {
manager.initiateArticle();
verify(database).addListener(any(ArticleListener.class));
}
}
public class SampleBaseTestCase {
@Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
}
In the example above annotations are used to create fake objects and a real object which uses them. The end result is very similar to using an AutoMocking container.
What about NUnit?
I wanted to see if I can create similar functionality in the .NET world. At first I’ve tried using PostSharp but unfortunately the test runners had problems finding the new injected code. Instead I’ve decided to use NUnit’s ITestAction – a simple AOP for unit tests that seemed like a good fit.
NUnit has had the ability to execute code upon these events by decorating fixture classes and methods with the appropriate NUnit- provided attributes. Action Attributes allow the user to create custom attributes to encapsulate specific actions for use before or after any test is run.
I’ve created a simple attribute to mark fields that I wanted faked and named it (FakeItAttribute) and create a new attribute to enable discovery and creation of fake object for fields with that attribute:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)]
public abstract class AutoFakeAttributeBase : Attribute, ITestAction
{
private readonly IFakeHelper _fakeHelper;
protected AutoFakeAttributeBase(IFakeHelper fakeHelper)
{
_fakeHelper = fakeHelper;
}
private IEnumerable<FieldInfo> _testFields;
public void BeforeTest(TestDetails details)
{
var isTestFixture = details.Method == null;
if (isTestFixture)
{
DiscoverFieldsToFake(details);
return;
}
foreach (var testField in _testFields)
{
var fakeObject = _fakeHelper.DynamicallyCreateFakeObject(testField.FieldType);
testField.SetValue(details.Fixture, fakeObject);
}
}
public void AfterTest(TestDetails details)
{
}
private void DiscoverFieldsToFake(TestDetails details)
{
_testFields = details.Fixture.GetType()
.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(testField => testField.CustomAttributes.Any(data => data.AttributeType == typeof(FakeItAttribute)));
}
public ActionTargets Targets
{
get { return ActionTargets.Test | ActionTargets.Suite; }
}
}
I’ve inherited that attribute with AutoFakeItEasyAttribute that uses FakeItEasy and reflection to create fake objects.
And now I can write tests that automatically create fake objects by adding that
attribute.
[TestFixture, AutoFakeItEasy]
public class UsingSimpleClassTests
{
[FakeIt]
private IDependency _fakeDependency;
// Not faked
private IDependency _uninitializedDependency;
[Test]
public void FakesCreatedAutomatically()
{
Assert.That(_fakeDependency, Is.Not.Null);
}
[Test]
public void FieldsWithoutAttributesAreNotInitialized()
{
Assert.That(_uninitializedDependency, Is.Null);
}
}
Quite cool, and yes it’s on GitHub.
Now what
I’m not sure if I like the use of fields in unit tests. I’ve seen it misused to create unreadable (and unmaintainable) tests more often than not – but at least now the fake fields are will be initialized between test runs. Now I know it’s possible to create AutoFaking using attributes and I considering adding more features until I fully implement
AutoFakes.
Until then – happy coding…
Reference: | Automocking fields using NUnit from our NCG partner Dror Helper at the Helper Code blog. |