How to Include associated entities - unit-testing

I want to create test case for below method "GetByEmail".
public User GetByEmail(string email, bool includeUserRoles = false, bool includeUserType = false)
{
Expression<Func<User>> whereClause = u => u.Email == email;
return GetQuery(whereClause, includeUserRoles, includeUserType) .FirstOrDefault();
}
private IQueryable<User> GetQuery(Expression<Func<User>> whereClause,
bool includeUserRoles = false, bool includeUserType = false)
{
IQueryable<User> query = base.GetQuery(whereClause);
if (includeUserRoles)
query = query.Include(u => u.UserRoles);
if (includeUserType)
query = query.Include(u => u.UserType);
return query;
}
protected IQueryable<T> GetQuery<T>(Expression<Func<T>> predicate) where T : EntityBase
{
return predicate != null ?
CreateObjectSet<T>().Where(predicate) :
CreateObjectSet<T>();
}
protected IObjectSet<T> CreateObjectSet<T>() where T : EntityBase
{
return _context.CreateObjectSet<T>();
}
public static IQueryable<T> Include<T>(this IQueryable<T> source, Expression<Func<T>> property)
{
var objectQuery = source as ObjectQuery<T>;
if (objectQuery != null)
{
var propertyPath = GetPropertyPath(property);
return objectQuery.Include(propertyPath);
}
return source;
}
Below is my test case method -
[Fact]
private void GetByEmail_PassedEmailAddress_RelatedUser()
{
//Created fake context
var fakeContext = Isolate.Fake.Instance<Entities>();
//Created fake Repository and passed fakeContext to it
var fakeRepository = Isolate.Fake.Instance<Repository>(Members.CallOriginal, ConstructorWillBe.Called, fakeContext);
//Created fake in memory collection of User
var fakeUsers = GetUsers();
Isolate.WhenCalled(() => fakeContext.Context.CreateObjectSet<User>())
.WillReturnCollectionValuesOf(fakeUsers);
var User = Isolate.Invoke.Method(fakeRepository, "GetByEmail", "abc#xyz.com", true, true);
Assert.True(User != null);
}
In the above test case method I successfully get the user with passed email but not able to include other entities of associated user.
Kindly let me know, how can I include other entities with associated User.

Include is leaky abstraction - it works only with EF and linq-to-entities and cannot be successfully used with linq-to-objects. You know that your unit test needs populated relations so your GetUsers method must prepare that data. That is a point of mocking / faking - you don't think about internal implementation of mocked method. You simply return what should be returned.
Btw. what is the point of your test? It looks like you are trying to test a mock - that is wrong. Mock provides correct data and you only need it to test another feature dependent on mocked component.

Related

Is it useless to mock an interface's behavior if it's not to be called in the test

Do i need to mock interfaces that does not call, for instance user name and password field is empty? I'm trying to write test first but confused if mocks should be used.
My login test
private val authRepository: AuthRepository = mockk()
private val userManager: AccountManager = mockk()
private lateinit var authUseCase: AuthUseCase
#BeforeEach
fun setUp() {
clearMocks(authRepository)
clearMocks(userManager)
authUseCase = AuthUseCase(authRepository, userManager)
}
/**
* Scenario: Login check with empty fields:
* * Given I am on the login page
* * When I enter empty username
* And I enter empty password
* And I click on the "Login" button
* * Then I get empty fields error.
*/
#Test
fun `Empty fields result empty fields error`() {
// Given
// When
val expected = authUseCase.login("", "", false)
// Then
verify(exactly = 0) {
authRepository.login(or(any(), ""), or(any(), ""), any())
}
expected assertEquals EMPTY_FIELD_ERROR
}
Do i have to mock interface for the given part of the test or AccountManager even though they are not called since user name and/or fields are empty?
This is the final version of login method i intend to write after tests
class AuthUseCase(
private val authRepository: AuthRepository,
private val accountManager: AccountManager
) {
private var loginAttempt = 1
/*
STEP 1: Throw exception for test to compile and fail
*/
// fun login(
// userName: String,
// password: String,
// rememberMe: Boolean = false
// ): AuthenticationState {
// throw NullPointerException()
// }
/*
STEP3: Check if username or password is empty
*/
// fun login(
// userName: String,
// password: String,
// rememberMe: Boolean = false
// ): AuthenticationState {
//
//
// if (userName.isNullOrBlank() || password.isNullOrBlank()) {
// return EMPTY_FIELD_ERROR
// }else {
// throw NullPointerException()
// }
//
// }
/**
* This is the final and complete version of the method.
*/
fun login(
userName: String,
password: String,
rememberMe: Boolean
): AuthenticationState {
return if (loginAttempt >= MAX_LOGIN_ATTEMPT) {
MAX_NUMBER_OF_ATTEMPTS_ERROR
} else if (userName.isNullOrBlank() || password.isNullOrBlank()) {
EMPTY_FIELD_ERROR
} else if (!checkUserNameIsValid(userName) || !checkIfPasswordIsValid(password)) {
INVALID_FIELD_ERROR
} else {
// Concurrent Authentication via mock that returns AUTHENTICATED, or FAILED_AUTHENTICATION
val authenticationPass =
getAccountResponse(userName, password, rememberMe)
return if (authenticationPass) {
loginAttempt = 0
AUTHENTICATED
} else {
loginAttempt++
FAILED_AUTHENTICATION
}
}
}
private fun getAccountResponse(
userName: String,
password: String,
rememberMe: Boolean
): Boolean {
val authResponse =
authRepository.login(userName, password, rememberMe)
val authenticationPass = authResponse?.authenticated ?: false
authResponse?.token?.let {
accountManager.saveToken(it)
}
return authenticationPass
}
private fun checkUserNameIsValid(field: String): Boolean {
return field.length >15 && field.endsWith("#example.com")
}
private fun checkIfPasswordIsValid(field: String): Boolean {
return field.length in 6..10
}
}
Should i only mock when all other states and passed i get a mock response from repository and interaction with account manager occurs?
What should be given section of the test?
Edit:
I updated given section of this test to
#Test
fun `Empty fields result empty fields error`() {
// Given
every { authRepository.login(or(any(), ""), or(any(), "")) } returns null
// When
val expected = authUseCase.login("", "", false)
// Then
verify(exactly = 0) { authRepository.login(or(any(), ""), or(any(), "")) }
expected assertThatEquals EMPTY_FIELD_ERROR
}
Is there something wrong with this kind of behavior testing?
I would suggest that you don't need the verify in the "Empty fields result empty fields error" test. I would also suggest you write separate tests for each empty field. If you were doing strict TDD you would be testing each condition as you wrote the code. i.e.
'Empty username should error" would be the first test and the first condition tested, then "Empty password should error" the next (after you have done two separate written your second test your code may look like
if (userName.isNullOrBlank()) {
return EMPTY_FIELD_ERROR
}
if (password.isNullOrBlank() {
return EMPTY_FIELD_ERROR
}
Once both the tests above pass you could refactor to
if (userName.isNullOrBlank() || password.isNullOrBlank()) {
EMPTY_FIELD_ERROR
}
Once you start testing the conditional statements for checkUserNameIsValid and checkIfPasswordIsValid, you would need to introduce the authRepository and accountManager to your class (constructor injection) and then you would need to start mocking the calls as you use them. Generally mocking frameworks will fake an object (i.e. the code will run but won't return any meaningful result). You should aim to return actual mock data when you want to test specific behavior i.e. you should be returning a valid object from the authRepository.login when you are testing for a successful login. Generally I stay away from using setup methods in the #BeforeEach and use either a factory method or builder to create my class under test. I am unfamiliar with the kotlin syntax so can at best do some sudo code to demonstrate how your builder or factory functions may look like.
// overloaded factory function
fun create() {
val authRepository: AuthRepository = mockk()
val userManager: AccountManager = mockk()
return AuthUseCase(authRepository, userManager);
}
fun create(authRepository: AuthRepository) {
val userManager: AccountManager = mockk()
return AuthUseCase(authRepository, userManager);
}
fun create(authRepository: AuthRepository, userManager: AccountManager) {
return AuthUseCase(authRepository, userManager);
}
You will need to have a look at how to create a builder in kotlin but the end result you would be looking for is that the builder always starts setting the dependencies for you class under test as mocks that do nothing but allows you to change those mocks.
e.g.
AuthUseCase authUseCase = AuthUseCaseBuilder.Create().WithAuthRepository(myMockAuthRepository).Build();
One final thing. I purposely left out discussing loginAttempt check above as to me it looks like the AuthUseCase is a service class that will be used by multiple users and live for the lifetime of the request in which case you don't want to maintain state within the class (i.e. the loginAttempt variable has the same lifetime as the class). It would be better to record the attempts per username in a database table and the attempt count would need to be reset after each successful login.
Hope this helps.

Mocking IDocumentQuery in Unit Test that uses Linq queries

I am writing unit tests for DocumentDBRepository but I got a null reference exception. I use Moq framework and XUnit.
Here's my methods in DocumentDBRepository class.
public class DocumentDBRepository<T> : IRepository<T> where T: class
{
private static string DatabaseId;
private static string CollectionId;
private static IDocumentClient client;
public DocumentDBRepository(IDocumentClient documentClient, string databaseId, string collectionId)
{
DatabaseId = databaseId;
CollectionId = collectionId;
client = documentClient;
CreateDatabaseIfNotExistsAsync().Wait();
CreateCollectionIfNotExistsAsync().Wait();
}
public async Task<IDocumentQuery<T>> GetQuery(Expression<Func<T, bool>> predicate)
{
try
{
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),
new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true })
.Where(predicate)
.AsDocumentQuery();
return query;
}
catch (Exception e) {
throw;
}
}
public async Task<IEnumerable<T>> GetEntities(IDocumentQuery<T> query)
{
try
{
List<T> results = new List<T>();
while (query.HasMoreResults)
{
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
}
catch (Exception e)
{
throw;
}
}
}
Here's my test code:
public interface IFakeDocumentQuery<T> : IDocumentQuery<T>, IOrderedQueryable<T>
{
}
[Fact]
public async virtual Task Test_GetBooksById()
{
var expected = new List<Book> {
new Book { ID = "123", Description = "HarryPotter"},
new Book { ID = "124", Description = "HarryPotter2"} };
var response = new FeedResponse<Book>(expected);
var mockDocumentQuery = new Mock<IFakeDocumentQuery<Book>>();
mockDocumentQuery.SetupSequence(_ => _.HasMoreResults)
.Returns(true)
.Returns(false);
mockDocumentQuery.Setup(_ => _.ExecuteNextAsync<Book>(It.IsAny<CancellationToken>()))
.ReturnsAsync(response);
var client = new Mock<IDocumentClient>();
client.Setup(_ => _.CreateDocumentQuery<Book>(It.IsAny<Uri>(), It.IsAny<FeedOptions>()))
.Returns(mockDocumentQuery.Object);
var documentsRepository = new DocumentDBRepository<Book>(client.Object, "123", "123");
//Act
var query = await documentsRepository.GetQuery(t => t != null);
var entities = await documentsRepository.GetEntities(query);
//Assert
if (entities != null)
{
entities.Should().BeEquivalentTo(expected);
}
}
Here's the error message after running the test method:
Message: System.NullReferenceException : Object reference not set to
an instance of an object.
When I stepped through the code, the error happens right after the the test code called GetQuery() method:
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),
new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true })
.Where(predicate)
.AsDocumentQuery();
Here's my thought process: when I stepped through the entire code, I do not see any null variables. But in the 'response' variable from the second line of the test method, it does show a lot of the properties are null exception but result view shows the 'expected' variable.
My question is, is it because of the response variable that caused the null reference exception? Or somewhere else?
PS: Test code reference from here
I also tried turning on the Mock behavior to strict and saw this error message.
Message: System.AggregateException : One or more errors occurred.
(IDocumentClient.ReadDatabaseAsync(dbs/123, null) invocation failed
with mock behavior Strict. All invocations on the mock must have a
corresponding setup.)
---- Moq.MockException : IDocumentClient.ReadDatabaseAsync(dbs/123, null) invocation failed with mock behavior Strict. All invocations on
the mock must have a corresponding setup.
As suspected the problem is .Where(predicate). I ran a test with the provided example and removed the .Where clause and it executed to completion.
The fake interface inherits from both IOrderedQueryable and IDocumentQuery. The issue is that the Where is converting it back to a plain IEnumerable because of the List data source and the AsDocumentQuery is crapping out as it is expecting an IDocumentQuery
I am not a fan of tightly coupling to APIs I can't control. I would abstract my way around such implementation details for that very reason.
The work around involved having to provide a fake Linq IQueryProvider to bypass any queries and return a type that derives from IDocumentQuery so as to allow AsDocumentQuery to behave as intended.
But first I refactored GetEntities and made GetQuery private to stop the repository from being a leaky abstraction.
private IDocumentQuery<T> getQuery(Expression<Func<T, bool>> predicate) {
var uri = UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId);
var feedOptions = new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true };
var queryable = client.CreateDocumentQuery<T>(uri, feedOptions);
IQueryable<T> filter = queryable.Where(predicate);
IDocumentQuery<T> query = filter.AsDocumentQuery();
return query;
}
public async Task<IEnumerable<T>> GetEntities(Expression<Func<T, bool>> predicate) {
try {
IDocumentQuery<T> query = getQuery(predicate);
var results = new List<T>();
while (query.HasMoreResults) {
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
} catch (Exception e) {
throw;
}
}
Note that getQuery is not doing anything async so it should not be returning a Task<> anyway.
Next in the test the mocked IDocumentQuery was set up to allow the test to flow to completion. This was done by providing a mocked IQueryProvider the would return the mocked IDocumentQuery when Linq queries are invoked against it. (which was the cause of the problem to begin with)
public async virtual Task Test_GetBooksById() {
//Arrange
var id = "123";
Expression<Func<Book, bool>> predicate = t => t.ID == id;
var dataSource = new List<Book> {
new Book { ID = id, Description = "HarryPotter"},
new Book { ID = "124", Description = "HarryPotter2"}
}.AsQueryable();
var expected = dataSource.Where(predicate);
var response = new FeedResponse<Book>(expected);
var mockDocumentQuery = new Mock<IFakeDocumentQuery<Book>>();
mockDocumentQuery
.SetupSequence(_ => _.HasMoreResults)
.Returns(true)
.Returns(false);
mockDocumentQuery
.Setup(_ => _.ExecuteNextAsync<Book>(It.IsAny<CancellationToken>()))
.ReturnsAsync(response);
var provider = new Mock<IQueryProvider>();
provider
.Setup(_ => _.CreateQuery<Book>(It.IsAny<System.Linq.Expressions.Expression>()))
.Returns((Expression expression) => {
if (expression != null) {
dataSource = dataSource.Provider.CreateQuery<Book>(expression);
}
mockDocumentQuery.Object;
});
mockDocumentQuery.As<IQueryable<Book>>().Setup(x => x.Provider).Returns(provider.Object);
mockDocumentQuery.As<IQueryable<Book>>().Setup(x => x.Expression).Returns(() => dataSource.Expression);
mockDocumentQuery.As<IQueryable<Book>>().Setup(x => x.ElementType).Returns(() => dataSource.ElementType);
mockDocumentQuery.As<IQueryable<Book>>().Setup(x => x.GetEnumerator()).Returns(() => dataSource.GetEnumerator());
var client = new Mock<IDocumentClient>();
client.Setup(_ => _.CreateDocumentQuery<Book>(It.IsAny<Uri>(), It.IsAny<FeedOptions>()))
.Returns(mockDocumentQuery.Object);
var documentsRepository = new DocumentDBRepository<Book>(client.Object, "123", "123");
//Act
var entities = await documentsRepository.GetEntities(predicate);
//Assert
entities.Should()
.NotBeNullOrEmpty()
.And.BeEquivalentTo(expected);
}
This allowed the test to be exercised to completion, behave as expected, and pass the test.

How to write unit test for ActionFilter when using Service Locator

I am planning to write an ActionFilter for business validation and in which some services will be resolved via Service Locator(I know this is not good practice and as far as possible i avoid Service Locator pattern, but for this case i want to use it).
OnActionExecuting method of the filter is something like this:
public override void OnActionExecuting(ActionExecutingContext actionContext)
{
// get validator for input;
var validator = actionContext.HttpContext.RequestServices.GetService<IValidator<TypeOfInput>>();// i will ask another question for this line
if(!validator.IsValid(input))
{
//send errors
}
}
Is it possible to write unit test for above ActionFilterand how?
Here is an sample on how to create a mock (using XUnit and Moq framework) to verify that the IsValid method is called and where the mock returns an false.
using Dealz.Common.Web.Tests.Utils;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using System;
using Xunit;
namespace Dealz.Common.Web.Tests.ActionFilters
{
public class TestActionFilter
{
[Fact]
public void ActionFilterTest()
{
/****************
* Setup
****************/
// Create the userValidatorMock
var userValidatorMock = new Mock<IValidator<User>>();
userValidatorMock.Setup(validator => validator
// For any parameter passed to IsValid
.IsValid(It.IsAny<User>())
)
// return false when IsValid is called
.Returns(false)
// Make sure that `IsValid` is being called at least once or throw error
.Verifiable();
// If provider.GetService(typeof(IValidator<User>)) gets called,
// IValidator<User> mock will be returned
var serviceProviderMock = new Mock<IServiceProvider>();
serviceProviderMock.Setup(provider => provider.GetService(typeof(IValidator<User>)))
.Returns(userValidatorMock.Object);
// Mock the HttpContext to return a mockable
var httpContextMock = new Mock<HttpContext>();
httpContextMock.SetupGet(context => context.RequestServices)
.Returns(serviceProviderMock.Object);
var actionExecutingContext = HttpContextUtils.MockedActionExecutingContext(httpContextMock.Object, null);
/****************
* Act
****************/
var userValidator = new ValidationActionFilter<User>();
userValidator.OnActionExecuting(actionExecutingContext);
/****************
* Verify
****************/
// Make sure that IsValid is being called at least once, otherwise this throws an exception. This is a behavior test
userValidatorMock.Verify();
// TODO: Also Mock HttpContext.Response and return in it's Body proeprty a memory stream where
// your ActionFilter writes to and validate the input is what you desire.
}
}
class User
{
public string Username { get; set; }
}
class ValidationActionFilter<T> : IActionFilter where T : class, new()
{
public void OnActionExecuted(ActionExecutedContext context)
{
throw new NotImplementedException();
}
public void OnActionExecuting(ActionExecutingContext actionContext)
{
var type = typeof(IValidator<>).MakeGenericType(typeof(T));
var validator = (IValidator<T>)actionContext.HttpContext
.RequestServices.GetService<IValidator<T>>();
// Get your input somehow
T input = new T();
if (!validator.IsValid(input))
{
//send errors
actionContext.HttpContext.Response.WriteAsync("Error");
}
}
}
internal interface IValidator<T>
{
bool IsValid(T input);
}
}
HttpContextUtils.cs
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Collections.Generic;
namespace Dealz.Common.Web.Tests.Utils
{
public class HttpContextUtils
{
public static ActionExecutingContext MockedActionExecutingContext(
HttpContext context,
IList<IFilterMetadata> filters,
IDictionary<string, object> actionArguments,
object controller
)
{
var actionContext = new ActionContext() { HttpContext = context };
return new ActionExecutingContext(actionContext, filters, actionArguments, controller);
}
public static ActionExecutingContext MockedActionExecutingContext(
HttpContext context,
object controller
)
{
return MockedActionExecutingContext(context, new List<IFilterMetadata>(), new Dictionary<string, object>(), controller);
}
}
}
As you can see, it's quite a mess, you need to create plenty of mocks to simulate different responses of the actuall classes, only to be able to test the ActionAttribute in isolation.
I like #Tseng's above answer but thought of giving one more answer as his answer covers more scenarios (like generics) and could be overwhelming for some users.
Here I have an action filter attribute which just checks the ModelState and short circuits(returns the response without the action being invoked) the request by setting the Result property on the context. Within the filter, I try to use the ServiceLocator pattern to get a logger to log some data(some might not like this but this is an example)
Filter
public class ValidationFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<ValidationFilterAttribute>>();
logger.LogWarning("some message here");
context.Result = new JsonResult(new InvalidData() { Message = "some messgae here" })
{
StatusCode = 400
};
}
}
}
public class InvalidData
{
public string Message { get; set; }
}
Unit Test
[Fact]
public void ValidationFilterAttributeTest_ModelStateErrors_ResultInBadRequestResult()
{
// Arrange
var serviceProviderMock = new Mock<IServiceProvider>();
serviceProviderMock
.Setup(serviceProvider => serviceProvider.GetService(typeof(ILogger<ValidationFilterAttribute>)))
.Returns(Mock.Of<ILogger<ValidationFilterAttribute>>());
var httpContext = new DefaultHttpContext();
httpContext.RequestServices = serviceProviderMock.Object;
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
var actionExecutingContext = new ActionExecutingContext(
actionContext,
filters: new List<IFilterMetadata>(), // for majority of scenarios you need not worry about populating this parameter
actionArguments: new Dictionary<string, object>(), // if the filter uses this data, add some data to this dictionary
controller: null); // since the filter being tested here does not use the data from this parameter, just provide null
var validationFilter = new ValidationFilterAttribute();
// Act
// Add an erorr into model state on purpose to make it invalid
actionContext.ModelState.AddModelError("Age", "Age cannot be below 18 years.");
validationFilter.OnActionExecuting(actionExecutingContext);
// Assert
var jsonResult = Assert.IsType<JsonResult>(actionExecutingContext.Result);
Assert.Equal(400, jsonResult.StatusCode);
var invalidData = Assert.IsType<InvalidData>(jsonResult.Value);
Assert.Equal("some messgae here", invalidData.Message);
}

Mocking in Unit Tests

I am trying to test the AddCategory of the following CategoryService.
My problem is that I am having a hard time understanding what to mock/fake.
My attempt at the test is at the bottom.
I am using MOQ, xUnit and FluentAssertions.
I am using FluentValidation for the validators.
Category Service
public class CategoryService : ValidatingServiceBase, ICategoryService
{
private readonly IUnitOfWork unitOfWork;
private readonly IRepository<Category> categoryRepository;
private readonly IRepository<SubCategory> subCategoryRepository;
private readonly IValidationService validationService;
public CategoryService(
IUnitOfWork unitOfWork,
IRepository<Category> categoryRepository,
IRepository<SubCategory> subCategoryRepository,
IValidationService validationService)
: base(validationService)
{
this.unitOfWork = unitOfWork;
this.categoryRepository = categoryRepository;
this.subCategoryRepository = subCategoryRepository;
this.validationService = validationService;
}
public bool AddCategory(Category category)
{
var validationResult = validationService.Validate(category);
if (!validationResult.IsValid)
{
return false;
}
else
{
categoryRepository.Add(category);
return true;
}
}
public bool DoesCategoryExist(string categoryName)
{
return categoryRepository.Query().SingleOrDefault(x => x.Name == categoryName) != null;
}
}
Validation Service
public class ValidationService : ServiceBase, IValidationService
{
private readonly IValidatorFactory validatorFactory;
public ValidationService(IValidatorFactory validatorFactory)
{
Enforce.ArgumentNotNull(validatorFactory, "validatorFactory");
this.validatorFactory = validatorFactory;
}
public ValidationResult Validate<TEntity>(TEntity entity) where TEntity : class
{
var validator = validatorFactory.GetValidator<TEntity>();
return validator.Validate(entity);
}
}
Validator Factory
public class ValidatorFactory : IValidatorFactory
{
public IValidator GetValidator(Type type)
{
Enforce.ArgumentNotNull(type, "type");
return DependencyResolver.Current.GetService(typeof(IValidator<>).MakeGenericType(type)) as IValidator;
}
public IValidator<T> GetValidator<T>()
{
return DependencyResolver.Current.GetService<IValidator<T>>();
}
}
Category Validator
public class CategoryValidator : AbstractValidator<Category>
{
public CategoryValidator(ICategoryService service)
{
RuleFor(x => x.Name)
.NotEmpty()
.Must((category, name) =>
{
return service.DoesCategoryExist(name);
});
}
}
Unit Test Attempt
[Fact]
public void AddCategory_Should_ReturnTrue()
{
var category = new Category() { Name = "Cat1" };
var unitOfWork = new Mock<IUnitOfWork>();
var categoryRepo = new Mock<IRepository<Category>>();
var subCategoryRepo = new Mock<IRepository<SubCategory>>();
var mockCategoryService = new Mock<ICategoryService>();
var categoryValidator = new CategoryValidator(mockCategoryService.Object);
var validatorFactory = new Mock<IValidatorFactory>();
validatorFactory.Setup(x => x.GetValidator<CategoryValidator>()).Returns(categoryValidator as IValidator<CategoryValidator>);
var validationService = new ValidationService(validatorFactory.Object);
var categoryService = new CategoryService(
unitOfWork.Object,
categoryRepo.Object,
subCategoryRepo.Object,
validationService);
categoryService.AddCategory(category);
}
Well for the AddCategory method, I think you really only need two mocks, one for the ValidationService, and one for the CategoryRepository, because the other dependencies aren't exercised in that function and therefore are irrelevant
(the story might be different of course if your ctor throws on null arguments but in this case I think you are OK - albeit you might consider adding these checks in the future :)
Anyway, being pedantic, I'd nearly be inclined to write two (or more - maybe one for null input to verify it throws or returns false or whatever) "unit" tests for this function;
One to verify that given an invalid category, the function returns false,
One to verify that given a valid category, the function calls Add on the CategoryRepository dependency.
So it would look like this (sorry, this is using MSTest syntax as I'm not familiar with xUnit but it's the same idea). Also have not tested below for typos, etc :)
public void AddCategory_InvalidCategory_ShouldReturnFalse()
{
//Arrange
var mockValidator = new Mock<IValidator>();
//no matter what we pass to the validator, it will return false
mockValidator.Setup(v=>v.Validate(It.IsAny<Category>()).Returns(false);
var sut= new CategoryService(null,null,null,mockValidator.Object);
bool expected = false;
//ACT
bool actual = sut.AddCategory(new Category());
//ASSERT
Assert.AreEqual(expected,actual,"Validator didn't return false as expected");
}
public void AddCategory_ValidCategory_ShouldCallRepositoryAdd()
{
//Arrange
var mockValidator = new Mock<IValidator>();
//no matter what we pass to the validator, it will return true
mockValidator.Setup(v=>v.Validate(It.IsAny<Category>()).Returns(true);
var mockRepo = new Mock<IRepository<SubCategory>>();
mockRepo.Setup(r=>r.Add(It.IsAny<Category>())); //do not know or care what happens as this is a void method.
var sut= new CategoryService(null,mockRepo.Object,null,mockValidator.Object);
bool expected = false;
//ACT
bool actual = sut.AddCategory(new Category());
//ASSERT
mockRepo.Verify(r=>r.Add(It.IsAny<Category>(),Times.Exactly(1),"Repo ADD method not called or called too many times, etc");
Assert.AreEqual(expected,actual,"Add was called BUT the AddCategoryMethod didn't return true as expected"); //and of course you could be totally pedantic and create a new test method for that last assert ;)
}
The reason I favour this approach is because it forces you to consider the behaviour of the method under test, as well as ensuring that you don't involve any dependencies that are not being tested plus it means your test methods only create exactly what they need to in order to run the tests (and of course you can create some setup/teardown helpers to pre-create those mocks for you);
Of course you could put all the above into a single method but for the sake of saving a few LOC I hope you'll agree that having two separate tests to verify two separate behaviours is a more robust approach.
Just my 2c. hope it helps!

CommonDomain - how to unit test aggregate root

I have a small system that uses Jonathan Oliver's CommonDomain and EventStore.
How can I unit test my aggregate roots in order to verify that correct events are raised?
Consider following aggregate root:
public class Subscriber : AggregateBase
{
private Subscriber(Guid id)
{
this.Id = id;
}
private Subscriber(Guid id, string email, DateTimeOffset registeredDate)
: this(id)
{
this.RaiseEvent(new NewSubscriberRegistered(this.Id, email, registeredDate));
}
public string Email{ get; private set; }
public DateTimeOffset RegisteredDate { get; private set; }
public static Subscriber Create(Guid id, string email, DateTimeOffset registeredDate)
{
return new Subscriber(id, email, registeredDate);
}
private void Apply(NewSubscriberRegistered #event)
{
this.Email = #event.Email;
this.RegisteredDate = #event.RegisteredDate;
}
}
I would like to write a following test:
// Arrange
var id = Guid.NewGuid();
var email = "test#thelightfull.com";
var registeredDate = DateTimeOffset.Now;
// Act
var subscriber = Subscriber.Create(id, email, registeredDate);
// Assert
var eventsRaised = subscriber.GetEvents(); <---- How to get the events?
// Assert that NewSubscriberRegistered event was raised with valid data
I could set up whole EventStore with memory persistence and synchronous dispatcher, hook up mock event handler and store any published events for verification, but it seems a bit of overkill.
There is an interface IRouteEvents in CommonDomain. Looks like I could mock it to get the events directly from AggregateBase but how would I actually pass it to my Subscriber class? I don't want to 'pollute' my domian with testing-related code.
I've found out that AggregateBase explicitly implements IAggregate interface, which exposes ICollection GetUncommittedEvents(); method.
So the unit test looks like that:
var eventsRaised = ((IAggregate)subscriber).GetUncommittedEvents();
and no dependency on EventStore is required.
I just pushed up NEventStoreExample with code I gathered in various places (StackOverflow, Documently, Greg Young's skillcast).
It's a very basic implementation of NEventStore that uses CommonDomain to rebuild aggregate state and an EventSpecification base test class to test aggregate behaviour.
Here is a fairly simple test fixture that uses NUnit and ApprovalTests to test CommonDomain aggregate roots . (ApprovalTests is not required - just kinda makes life simple).
The assumption is that
1) the fixture is instantiated with an aggregate (perhaps already set in a certain state) along with a series of 'given' events to be applied.
2) the test will then invoke a specific command handler as part of the TestCommand method - current expectation is a Func that returns the command that is handled
3) the aggregate snapshot, commands, and events all contain 'rich' ToString methods
The TestCommand method then compares the expected with the approved interactions within the aggregate.
public class DomainTestFixture<T>
where T : AggregateBase
{
private readonly T _agg;
private readonly StringBuilder _outputSb = new StringBuilder();
public DomainTestFixture(T agg, List<object> giveEvents)
{
_agg = agg;
_outputSb.AppendLine(string.Format("Given a {0}:", agg.GetType().Name));
giveEvents.ForEach(x => ((IAggregate) _agg).ApplyEvent(x));
_outputSb.AppendLine(
giveEvents.Count == 0
? string.Format("with no previously applied events.")
: string.Format("with previously applied events:")
);
giveEvents.ForEach(x => _outputSb.AppendLine(string.Format(" - {0}", x)));
((IAggregate) _agg).ClearUncommittedEvents();
var snapshot = ((IAggregate) _agg).GetSnapshot();
_outputSb.AppendLine(string.Format("which results in the state: {0}", snapshot));
}
public void TestCommand(Func<T, object> action)
{
var cmd = action.Invoke(_agg);
_outputSb.AppendLine(string.Format("When handling the command: {0}", cmd));
_outputSb.AppendLine(string.Format("Then the {0} reacts ", _agg.GetType().Name));
var raisedEvents = ((IAggregate) _agg).GetUncommittedEvents().Cast<object>().ToList();
_outputSb.AppendLine(
raisedEvents.Count == 0
? string.Format("with no raised events")
: string.Format("with the following raised events:")
);
raisedEvents.ForEach(x => _outputSb.AppendLine(string.Format(" - {0}", x)));
var snapshot = ((IAggregate) _agg).GetSnapshot();
var typ = snapshot.GetType();
_outputSb.AppendLine(string.Format("and results in the state: {0}", snapshot));
Approvals.Verify(_outputSb.ToString());
Assert.Pass(_outputSb.ToString());
}
}
and an example usage
[Test]
public void Test_Some_Aggregate_Handle_Command()
{
var aggId = Guid.Empty;
var tester = new DomainTestFixture<PartAggregate>(
new PartAggregate(aggId, null),
new List<object>()
{
new PartOrdered(),
new PartReceived()
}
);
tester.TestCommand(
(agg) =>
{
var cmd = new RejectPart();
agg.Handle(cmd);
return cmd;
});
}