Staging
Let's consider a small Student class
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public bool HasLicense { get; set; }
}
and the Security class, which checks licenses
public class Security
{
public bool CheckLicense(Student student)
{
return student.HasLicense;
}
}
We need to test the functionality of the CheckLicense method.
Standard test
Based on the experience of previous article, we could write something like
[Fact]
public void CheckLicense_Success()
{
var student = FakeFactory.Fixture.Create<Student>();
//
var result = security.CheckLicense(student);
//
Assert.True(result == student.HasLicense);
}
Looks good, but what if we complicate the situation and include a service in the scheme that issues student status according to the date of the request?
public interface IStudentLicenseStore
{
bool GetLicense(string name, DateTime date);
}
public class Security
{
private readonly IStudentLicenseStore _store;
public Security(IStudentLicenseStore store)
{
_store = store;
}
public bool CheckLicense(Student student, DateTime date)
{
return _store.GetLicense(student.Name, date);
}
}
Of course, bool looks strange here, but such a simplified model will do for our purposes. So how will our test change?
[Fact]
public void CheckLicense_Success()
{
var student = FakeFactory.Fixture.Create<Student>();
var serviceResult = FakeFactory.Fixture.Create<bool>();
var date = FakeFactory.Fixture.Create<DateTime>();
_store.Setup(s => s.GetLicense(student.Name, date)).Returns(serviceResult);
//
var result = security.CheckLicense(student, date);
//
Assert.True(result == serviceResult);
}
There is more and more input data and the test becomes bulky, although, in reality, the lines with the declaration of variables do not significant for us. Can this code be simplified?
AutoMoqDataAttribute
The Theory and AutoData attributes could come to the rescue, they themselves can provide a fake of all the data we need, but in this case we lose the ability to use the FakeFactory
class and configure fakes at our discretion. But these settings will be very useful to us in future articles, it is clearly not worth refusing them)
Let's create the AutoMoqDataAttribute class in the test project
public class AutoMoqDataAttribute : AutoDataAttribute
{
public AutoMoqDataAttribute() : base(FakeFactory.Fixture)
{
}
}
As you can see, this class is a lightweight wrapper over the AutoDataAttribute and its only task is to pass our fake settings, which will be very useful to us in the future.
Let's change our test
[Theory, AutoMoqData]
public void CheckLicense_Success(Student student, bool serviceResult, DateTime date)
{
_store.Setup(s => s.GetLicense(student.Name, date)).Returns(serviceResult);
//
var result = security.CheckLicense(student, date);
//
Assert.True(result == serviceResult);
}
The test looks much neater and is not overloaded with non-essential data, win?
InlineAutoDataAttribute
When writing tests, sometimes there are situations when automatically generated fakes do not meet the requirements of the application, so Student
may have a Phone
field that is significant for the method under test. Normally we could use InlineData
, but the AutoMoqDataAttribute
blocks using of InlineData
and will not allow the specified values.
In order to solve this problem, let's add another support class.
public class InlineAutoMoqDataAttribute : InlineAutoDataAttribute
{
public InlineAutoMoqDataAttribute(params object[] objects) : base(new AutoMoqDataAttribute(), objects)
{
}
}
And again, this is a wrapper over a base class of AutoFixture
, two points are important for us.
- Passing
AutoMoqDataAttribute
will allow the desiredFakeFactory
instance to be used. - Passing
InlineData
parameters. With this approach, a pass will be performed on all incoming test parameters; the zero parameter will be taken from the zero index ofobjects
, the first from the first, and so on. Ifobjects
ends and parameters are still required,AutoMoqDataAttribute
will take effect and generate random fakes.
As a result, our test will look something like this:
[Theory]
[InlineAutoMoqData("123456789")]
public void CheckLicense_Success(string phone, Student student, bool serviceResult, DateTime date)
{
_store.Setup(s => s.GetLicense(student.Name, date)).Returns(serviceResult);
//
var result = security.CheckLicense(student, date);
//
Assert.True(result == serviceResult);
}
Note that AutoMoqDataAttribute
is not used directly in this case.
Conclusion
Using of the two described classes should, in my opinion, be included into the standard for organizing testing code, that is contributing to improved readability and simplification of writing tests.