I started doing some experimentation with unit testing so that we can include them in our domain layer. However i dont know if I'm following the right path, thus i'm going to explain what i'm currently doing to see if i'm on the right track. Basically the architecture is like the following there is Domain Layer containing domain models and domain services (ex. User class and UserService class). Then Domain layer communicates with the DAL which implements the Generic Repository pattern together with the Unit of Work. Each domain service class in it's constructor accepts an IUnitOfWork interface, like the following:
public class UserService: IUserService
{
private readonly IUnitOfWork _unitOfWork;
public UserService(IUnitOfWork unitOfwork)
{
this._unitOfWork = unitOfwork;
}
}
In order to creat the unit tests, i decided to go with FakeItEasy framework. So in a UserServiceTest class i did the following:-
private IUserService _userService;
private const int userID = 2013;
[TestInitialize]
public void Initialize()
{
_userService = A.Fake<IUserService>();
A.CallTo(() => _userService.GetUserById(userID)).Returns(new User
{
UserID = userID,
RegistrationDate = DateTime.Now,
});
}
[TestMethod]
public void GetUserByID()
{
var user = _userService.GetUserById(userID);
Assert.IsInstanceOfType(user, typeof(Domain.User));
Assert.AreEqual(userID, user.userID);
}
When I run the tests, they pass. Is it the correct way of implementing unit tests? Before I was trying a different approach however FakeItEasy was failing with a ProxyGenerator exception. What i was doing is this:-
[TestInitialize]
public void Initialize()
{
_unitOfWork = A.Fake<IUnitOfWork>();
A.CallTo(() => _unitOfWork.UserRepository.FindById(userID)).Returns(new UserDto
{
UserID = userID,
RegistrationDate = DateTime.Now,
});
AutoMapper.Mapper.CreateMap<UserDto, User();
}
[TestMethod]
public void GetUserByID()
{
var userService = new UserService(_unitOfWork);
var user = userService.GetUserById(userID);
Assert.IsInstanceOfType(user, typeof(Domain.User));
Assert.AreEqual(userID, user.userID);
}
And this was throwing the below exception:-
Result Message:
Initialization method Initialize threw exception. System.ArgumentNullException: System.ArgumentNullException: Value cannot be null.
Parameter name: callTarget.
Result StackTrace:
at FakeItEasy.Creation.ProxyGeneratorSelector.MethodCanBeInterceptedOnInstance(MethodInfo method, Object callTarget, String& failReason)
at FakeItEasy.Configuration.DefaultInterceptionAsserter.AssertThatMethodCanBeInterceptedOnInstance(MethodInfo method, Object callTarget)
at FakeItEasy.Configuration.FakeConfigurationManager.AssertThatMemberCanBeIntercepted(LambdaExpression callSpecification)
at FakeItEasy.Configuration.FakeConfigurationManager.CallTo[T](Expression`1 callSpecification)
at FakeItEasy.A.CallTo[T](Expression`1 callSpecification)
Any feedback would be greatly appreciated. Thanks!
I think your original (second, in the question) test was failing because
_unitOfWork.UserRepository
is coming back asnull
inInitialize
. Normally FakeItEasy will create an fake object when chained properties are used, but I'm guessing (I have to guess because I don't know anything about the type ofUserRepository
) thatUserRepository
's type is not fakeable. In that case, you'd get a null back from_unitOfWork.UserRepository
.Let me jump back to your second test (which was first in your question), then we'll return to what I think you might want to do here.
Looking at your test,
I see a flaw. You're invoking a method on
_userService
directly, but_userService
is a fake object, so the test doesn't actually involve any of the production code. It's really only exercising FakeItEasy.I think what we want is sort of a blended approach - something that will exercise the code in a real
UserService
, without worrying aboutUserRepository
. Maybe something similar to (and I'm not using a compiler here, and don't know what methods are onIUnitOfWork
so take this with a grain of salt)Or, if there's nothing useful on
IUnitOfWork
except forUserRepository
, then I think the next step would be to investigate why the type ofUserRepository
wasn't fakeable (if my guess was right) - is it sealed? Does it lack appropriate and accessible constructors?