How to set multiple objects to Mocking DbSet<T> - c#

I have my interface as below
public interface IAssessmentDbContext
{
DatabaseFacade Database { get; }
DbSet<Domain.Entities.Assessment> Assessments { get; }
DbSet<AssessmentType> AssessmentTypes { get; }
Task<int> SaveChangesAsync(CancellationToken cancellationToken);
DbSet<AssessmentType> GetAssessmentTypes();
}
and i'm trying to mock data as follows.
public class AssessmentsControllerTests
{
private readonly IFixture _fixture;
private readonly Mock<IAssessmentDbContext> dbContextMock;
private readonly Mock<IRequestClient<GetCandidates>> requestClientMock;
private readonly AssessmentsController controller;
//private readonly Mock<CancellationToken> cancellationTokenMock;
public AssessmentsControllerTests()
{
dbContextMock = _fixture.Freeze<Mock<IAssessmentDbContext>>();
var data1 = new List<Domain.Entities.AssessmentType>();
var data = new Domain.Entities.AssessmentType()
{
Id = new Guid("CD4FA15F-B882-4995-57A3-08DA9A133B59"),
AssessmentTypeName = "Big50",
AssessmentTypeText = "Big Fifty"
};
data1.Add(data);
DbSet<Domain.Entities.AssessmentType> myDbSet = GetQueryableMockDbSet(data1);
var dataAssessment = new List<Domain.Entities.Assessment>();
Domain.Entities.Assessment obj = new Domain.Entities.Assessment()
{
AssessmentTypeId = new Guid("CD4FA15F-B882-4995-57A3-08DA9A133B59"),
UserId = new Guid("1572EA1B-4CBD-4C05-D03C-08DA47BC4FD9"),
CreatedAt = DateTime.Now,
Id = Guid.NewGuid()
};
dataAssessment.Add(obj);
DbSet<Domain.Entities.Assessment> myDbSetAssessment = GetQueryableMockDbSet(dataAssessment);
dbContextMock.Setup(x=>x.GetAssessmentTypes()).Returns(myDbSet);
dbContextMock.Setup(x => x.Assessments).Returns(myDbSetAssessment);
controller = new AssessmentsController(dbContextMock.Object);
}
[Fact]
public void GetAssessments_ShouldReturnData_WhenWithUserId()
{
// Arrange
var userIdMock = _fixture.Create<Guid>();
CancellationToken token = _fixture.Create<CancellationToken>();
// Act
var result = controller.GetAssessments("1572EA1B-4CBD-4C05-D03C-08DA47BC4FD9", token);
// Assert
result.Should().NotBeNull();
result.Should().BeAssignableTo<IActionResult>();
}
private static DbSet<T> GetQueryableMockDbSet<T>(List<T> sourceList) where T : class
{
var queryable = sourceList.AsQueryable();
var dbSet = new Mock<DbSet<T>>();
dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());
dbSet.Setup(d => d.Add(It.IsAny<T>())).Callback<T>((s) => sourceList.Add(s));
return dbSet.Object;
}
}
}
How can I mockup both Assessments and AssessmentType in IAssessmentDbContext

Related

Moq a service or service interface with a db context for unit testing

So I've trying to test my webapp coded in .net core 5.0, and I have a classic MVC model with service classes. Now I am trying to unit test the solution with moq to mock my database, and my tests run, but I have just noticed that they are all wrong when I debug. It seems as if it doesn't actually connect to the mock service or database itself... I have gone for mocking the service interface but that doesn't seem to work still. Help would be greatly appreciated.
Service class:
public class InventoryService : IInventoryService
{
private readonly DBContext _db;
public InventoryService(DBContext db)
{
_db = db;
}
public List<Inventory> GetInventories(string id)
{
var inventories = (from i in _db.Inventories where i.userId.Equals(id) select i).ToList();
return inventories;
}
public void CreateInventory(Inventory newInventory)
{
_db.Inventories.Add(newInventory);
_db.SaveChanges();
}
public bool DeleteInventory(Guid Id, string UserId)
{
var inventory = _db.Inventories.Find(Id);
if (inventory == null)
return false;
if (inventory.userId != UserId)
return false;
//Delete items using item service
var items = from i in _db.Items where i.inventoryId.Equals(Id) select i;
foreach(var i in items)
{
_db.Items.Remove(i);
}
_db.Inventories.Remove(inventory);
_db.SaveChanges();
return true;
}
}
}
Service interface it uses:
public interface IInventoryService
{
public List<Inventory> GetInventories(string id);
public void CreateInventory(Inventory newInventory);
public bool DeleteInventory(Guid Id, string UserId);
}
}
Model:
public class Inventory
{
// The Id field is a unique identifier for a specific inventory
[Key]
public Guid Id { get; set; }
// The name of the Inventory. Required to have a value
[Required]
[DisplayName("Inventory Name")]
public string name { get; set; }
public string userId { get; set; }
}
}
Unit test:
public class InventoryTests
{
// Unit test defined for the get user inventories, a valid ID will always be passed in so no need for negative testing
[Fact]
public void Get_user_inventories_with_valid_id()
{
//ARRANGE
Guid theId1 = new("00000000-0000-0000-0000-000000000001");
Guid theId2 = new("00000000-0000-0000-0000-000000000002");
string u1 = "xxx";
string u2 = "yyy";
var mockIn = new Mock<IInventoryService>();
var data = new List<Inventory>
{
new Inventory { Id = theId1, name = "Mums 1", userId = u1},
new Inventory { Id = theId1, name = "Mums 1.2", userId = u1},
}.AsQueryable();
var mockSet = new Mock<DbSet<Inventory>>();
mockSet.As<IQueryable<Inventory>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Inventory>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Inventory>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Inventory>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
Mock<IInventoryService> myser = new Mock<IInventoryService>();
//ACT
myser.Setup(x => x.GetInventories(u1)).Returns(mockSet.Object.ToList());
var tinvs = myser.Object.GetInventories(u1);
//ASSERT
Assert.Equal(2 , tinvs.Count);
}
//Unit test for creating an inventory, a valid new inventory object will always be passed in so need for negative testing
[Fact]
public void Creating_An_Inventory()
{
//ARRANGE
Guid theId2 = new("00000000-0000-0000-0000-000000000001");
string u1 = "xxx";
string u2 = "yyy";
var mockIn = new Mock<IInventoryService>();
var data = new List<Inventory>
{
}.AsQueryable();
var mockSet = new Mock<DbSet<Inventory>>();
mockSet.As<IQueryable<Inventory>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Inventory>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Inventory>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Inventory>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
//ACT
Mock<IInventoryService> myser = new Mock<IInventoryService>();
myser.Setup(x => x.GetInventories(u1)).Returns(mockSet.Object.ToList());
Inventory thenew = new Inventory { Id = theId2, name = "Mums 2", userId = u2 };
myser.Object.CreateInventory(thenew);
//ASSERT
Assert.NotNull(data);
}
//Unit test for deleting an inventory. It will always be valid because a guid and user id will be passed in automatically
[Fact]
public void Deleting_An_Inventory()
{
//ARRANGE
Guid theId1 = new("00000000-0000-0000-0000-000000000001");
Guid theId2 = new("00000000-0000-0000-0000-000000000002");
string u1 = "123";
//var mockIn = new Mock<IInventoryService>();
var data = new List<Inventory>
{
new Inventory { Id = theId1, name = "testinv 1", userId = u1},
}.AsQueryable();
var mockSet = new Mock<DbSet<Inventory>>();
mockSet.As<IQueryable<Inventory>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Inventory>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Inventory>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Inventory>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
//ACT
Mock<IInventoryService> myser = new Mock<IInventoryService>();
myser.Setup(x => x.GetInventories(u1)).Returns(mockSet.Object.ToList());
myser.Object.DeleteInventory(theId1, u1);
var updInvs = myser.Object.GetInventories(u1);
int x = updInvs.Count;
//ASSERT
Assert.Equal(0, x);
}
}
}
I am fairly sure it is a minor error in my setup of the unit test's mocking. any help would be great.
So, if you want to test your InventoryService, you have to mock your dbContext as you do. Better use EF InMemoryDatabase like Microsoft advises https://learn.microsoft.com/en-us/ef/core/testing/#unit-testing
About your tests, I make a few modifications, to test InventoryService. It passes in debug mode.
public class InventoryService : IInventoryService
{
private readonly DBContext _db;
public InventoryService(DBContext db)
{
_db = db;
}
public List<Inventory> GetInventories(string id)
{
//i change inventories to _db.Set<Inventory> which able to be mock
var inventories = (from i in _db.Set<Inventory>() where i.userId.Equals(id) select i).ToList();
return inventories;
}
//ANOTHER CODE
}
[Fact]
public void Get_user_inventories_with_valid_id()
{
//ARRANGE
Guid theId1 = new("00000000-0000-0000-0000-000000000001");
Guid theId2 = new("00000000-0000-0000-0000-000000000002");
string u1 = "xxx";
string u2 = "yyy";
var mockIn = new Mock<IInventoryService>();
var data = new List<Inventory>
{
new Inventory { Id = theId1, name = "Mums 1", userId = u1 },
new Inventory { Id = theId1, name = "Mums 1.2", userId = u1 },
}.AsQueryable();
var mockSet = new Mock<DbSet<Inventory>>();
mockSet.As<IQueryable<Inventory>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Inventory>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Inventory>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Inventory>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
//HERE
var dbMock = new Mock<DBContext>();
dbMock.Setup(x => x.Set<Inventory>()).Returns(mockSet.Object);
var myser = new InventoryService(dbMock.Object);
//ACT
//AND HERE
var tinvs = myser.GetInventories(u1);
//ASSERT
Assert.Equal(2, tinvs.Count);
}
If you want to make InMemory db or test on real SQL db, you look at these questions.
Unit testing EF Core using in-memory database with an eager-loaded function
Unit testing with EF Core and in memory database

How to mock AbstractValidator

I am failing on the BeUniqueEmail with the error message (below). How can I mock this correctly? I have included the validator and the test below.
The tests passed when I removed the BeUnique email validation
Message:
System.NotImplementedException : The method or operation is not implemented.
public class CreateStudentCommandValidator : AbstractValidator<CreateStudentCommand>
{
private readonly IApplicationDbContext _context;
public CreateStudentCommandValidator(IApplicationDbContext context)
{
_context = context;
RuleFor(v => v.Email)
.NotEmpty().WithMessage("Email is required.")
.MaximumLength(30).WithMessage("Email must not exceed 30 characters.")
.MustAsync(BeUniqueEmail).WithMessage("The specified email already exists.");
}
public async Task<bool> BeUniqueEmail(CreateStudentCommand model, string email, CancellationToken cancellationToken)
{
bool emailExists = await _context.Students
.Where(x => x.Email == email)
.Where(x => !x.IsDeleted)
.CountAsync() > 0;
return !emailExists;
}
}
Testing
[Test]
public async Task CreateStudentCommand_Success()
{
var mockSet = new Mock<DbSet<Student>>();
var context = new Mock<IApplicationDbContext>();
context.Setup(m => m.Student).Returns(mockSet.Object);
var handler = new CreateStudentCommandHandler(context.Object);
var validator = new CreateStudentCommandValidator(context.Object);
//var mockedValidator = new Mock<IValidator<CreateStudentCommandValidator>>();
//var mock1 = new Mock<AbstractValidator<CreateStudentCommandValidator>>();
var command = new CreateStudentCommand
{
StudentType = "Test1",
Email = "Test1#email.com",
FirstName = "Test1",
LastName = "Test1",
IsActive = true
};
var result = await handler.Handle(command, new CancellationToken());
// Act
var validationResult = await validator.ValidateAsync(command);
// Assert
Assert.True(validationResult.IsValid);
Assert.IsInstanceOf<Guid>(result);
}
Using Moq, a solution to this could be as follows.
[Test]
public async Task TestValidation()
{
var context = Mock.Of<ApplicationDbContext>();
var validator = new CreateStudentCommandValidator(context);
var command = new CreateStudentCommand
{
StudentType = "Test1",
Email = "Test1#email.com",
FirstName = "Test1",
LastName = "Test1",
IsActive = true
};
var validationResult = await validator.ValidateAsync(command);
Assert.True(validationResult.IsValid);
}
Not knowing your Context, I implemented a simple dummy.
public interface IApplicationDbContext
{
List<Student> Students { get; set; }
}
public class ApplicationDbContext : IApplicationDbContext
{
public ApplicationDbContext()
{
Students = new List<Student>();
}
public List<Student> Students { get; set; }
}
This all depends on the use of Moq however, If you are using some other Mocking Service the Implementation will change.
*My Previous answer was intended to explain the implementation of Mocking Method Results. But in this case you do not need to mock the CreateStudentCommandValidator

Inline (override) mapping for nested types with AutoMapper

I would like to use AutoMapper and provide some values in runtime.
For example I have DTO & ViewModel.
One of the property doesn't exist in DTO and cannot be mapped directly by using Converters/Resolvers/Transformers;
namespace Lab.So.Sample
{
public class UserDto
{
public int UserId { get; set; }
public string UserCode { get; set; }
}
public class UserGroupDto
{
public List<UserDto> Users { get; set; }
}
public class UserViewModel
{
public int UserId { get; set; }
public string FullName { get; set; }
}
public class UserGroup
{
public List<UserViewModel> Users { get; set; }
}
class Program
{
static void Main(string[] args)
{
var src = new Fixture().Create<UserGroupDto>();
Mapper.Initialize(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.ValidateInlineMaps = false;
});
// How to hook mapping of nested object User->UserViewModel to provide value of FullName in runtime
var dst = Mapper.Map<UserGroupDto, UserGroup>(src);
}
}
}
I have some workaround how do this, but it's not human-friendly, as for me:
class Program
{
internal class UserViewModelFullNameResolver : IValueResolver<UserDto, UserViewModel, string>
{
public string Resolve(UserDto source, UserViewModel destination, string destMember, ResolutionContext context)
{
var names = context.Items["ctx.Names"] as IDictionary<int, string>;
if (names == null || !names.TryGetValue(source.UserId, out var fullName))
{
return null;
}
return fullName;
}
}
static void Main(string[] args)
{
var src = new Fixture().Create<UserGroupDto>();
Mapper.Initialize(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.ValidateInlineMaps = false;
cfg.CreateMap<UserDto, UserViewModel>()
.ForMember(d => d.FullName, opt => opt.MapFrom<UserViewModelFullNameResolver>());
});
var names = new Dictionary<int, string>
{
{ 10, "FullName-10" },
{ 20, "FullName-20" },
};
var dst = Mapper.Map<UserGroupDto, UserGroup>(src, opt=>opt.Items["ctx.Names"] = names);
}
}
In that workaround most inconvenient it's agreement of key names in opt.Items;
If something accidentally made typo it hard to investigate and fix;
I looking something like this:
var dst = Mapper.Map<UserGroupDto, UserGroup>(src, opt=>opt.Use(new UserViewModelFullNameResolver());
By other words, its defining an instance of resolver in runtime for each unique case;
Also I would accept, if I will have ability to define a hook to mapping particular type in graph of objects:
var dst = Mapper.Map<UserGroupDto, UserGroup>(src, opt=>opt.Hook<UserDto,UserViewModel>((s,d)=> { /* any logic to read external data */ });
An example of usage:
var srcA = readDataA();
var srcB = readDataB();
var dst = Mapper.Map<UserGroupDto, UserGroup>(
src,
opt=>opt.Hook<UserDto,UserViewModel>(
(s,d)=>
{
d.FullName = srcA + srcB;
});
Please suggest something, that would help to read data to accomplish mapping in cases when source doesn't have all required data for nested object in destination.
I was able to use existing API to archive my goal:
public static class InlineMappingExtensions
{
public static void EnableInlineMapping(this IMapperConfigurationExpression cfg)
{
cfg.ForAllMaps((t, i) =>
{
i.AfterMap((s, d, ctx) =>
{
ctx.ApplyInlineMap(s, d);
}
);
});
}
public static IMappingOperationOptions OnMap<TSrc, TDst>(this IMappingOperationOptions opts,
Action<TSrc, TDst> resolve)
{
var srcTypeName = typeof(TSrc);
var dstTypeName = typeof(TDst);
var ctxKey = $"OnMap_{srcTypeName}_{dstTypeName}";
opts.Items.Add(ctxKey, resolve);
return opts;
}
private static void ApplyInlineMap(this ResolutionContext opts, object src, object dst)
{
if (src == null)
{
return;
}
if (dst == null)
{
return;
}
var srcTypeName = src.GetType();
var dstTypeName = dst.GetType();
var ctxKey = $"OnMap_{srcTypeName}_{dstTypeName}";
if (!opts.Items.TryGetValue(ctxKey, out var inlineMap))
{
return;
}
var act = inlineMap as Delegate;
act?.DynamicInvoke(src, dst);
}
}
To enable it:
Mapper.Initialize(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.ValidateInlineMaps = false;
cfg.EnableInlineMapping();
});
Sample of usage:
// read data from external sources
var names = new Dictionary<int, string>
{
{ 10, "FullName-10" },
{ 20, "FullName-20" },
};
Action<UserDto, UserViewModel> mapA = (s, d) =>
{
if (names.TryGetValue(s.UserId, out var name))
{
d.FullName = name;
}
};
Action<UserGroupDto, UserGroup> mapB = (s, d) =>
{
if (DateTime.Now.Ticks > 0)
{
d.Users = null;
}
};
var dst = Mapper.Map<UserGroupDto, UserGroup>(src, opt => opt.OnMap(mapA).OnMap(mapB));

Xunit and Mock data with Moq

I'm new to unit testing, can anyone advise how to test public method (CreateUser) below using xUnit and Moq, thanks!
public async Task<bool> CreateUser(UserDTO newUser)
{
newUser.CustomerId = _userResolverService.GetCustomerId();
if (await CheckUserExists(newUser)) return false;
var salt = GenerateSalt(10);
var passwordHash = GenerateHash(newUser.Password, salt);
await _usersRepository.AddAsync(new User()
{
Role = newUser.Role,
CretedOn = DateTime.Now,
CustomerId = newUser.CustomerId,
Email = newUser.Email,
FirstName = newUser.FirstName,
LastName = newUser.LastName,
PasswordHash = passwordHash,
Salt = salt,
UpdatedOn = DateTime.Now
});
return true;
}
Private methods below
Check if user exists simply returns number of existing users
private async Task<bool> CheckUserExists(UserDTO user)
{
var users = await _usersRepository.GetAllAsync();
var userCount = users.Count(u => u.Email == user.Email);
return userCount > 0;
}
Hash Generation
private static string GenerateHash(string input, string salt)
{
var bytes = System.Text.Encoding.UTF8.GetBytes(input + salt);
var sha256 = SHA256.Create();
var hash = sha256.ComputeHash(bytes);
return ByteArrayToString(hash);
}
Salt Generaion
private static string GenerateSalt(int size)
{
var rng = RandomNumberGenerator.Create();
var buff = new byte[size];
rng.GetBytes(buff);
return Convert.ToBase64String(buff);
}
private static string ByteArrayToString(byte[] ba)
{
var hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
Thanks,
J
EDIT 1
This is what I have so far:
[Fact]
public async void CreateUser_True()
{
//arrange
var dataSource = new Mock<IRepository<User, int>>();
var userResolverService = new Mock<IUserResolverService>();
var tokenService = new Mock<ITokenService>();
var users = new List<User>();
users.Add(new User()
{
Email = "test#test.com",
CustomerId = 1
});
dataSource.Setup(m => m.GetAllAsync()).ReturnsAsync(users); // Error Here with converting async task to IEnumerable...
var accountService = new AccountService(dataSource.Object,userResolverService.Object,tokenService.Object);
//act
//assert
}
Main problem is that I have no idea how to Mock, set up behavior of private Method "CheckUserExists", as it's calling IRepository which is mocked for class, so will this in this case return my mock setup for GetAllAsync from this private method?
EDIT 2
public async Task<TEntity> AddAsync(TEntity entity)
{
if (entity == null)
{
throw new ArgumentNullException(nameof(entity));
}
_entities.Add(entity);
await _context.SaveChangesAsync();
return entity;
}
EDIT 3
[Fact]
public async void CreateUser_True()
{
//arrange
var users = new List<User>();
users.Add(new User()
{
Email = "test#test.com",
CustomerId = 1
});
_dataSource.Setup(m => m.GetAllAsync()).ReturnsAsync(users);
_dataSource.Setup(m => m.AddAsync(It.IsAny<User>())).Returns<User>(Task.FromResult);
var accountService = new AccountService(_dataSource.Object, _userResolverService.Object, _tokenService.Object);
//act
var result = await accountService.CreateUser(new UserDTO()
{
Email = "truetest#test.com"
});
var updatedUsersList = await _dataSource.Object.GetAllAsync();
var usersCount = updatedUsersList.Count();
//assert
Assert.True(result);
Assert.Equal(2, usersCount);
}
As the method being tested is async you need to setup all async dependencies to allow the method flow to completion. As for the private method, you want to setup the behavior of any dependencies that are used within that method, which in this case is the users repository.
[Fact]
public async Task CreateUser_True() {
//arrange
var usersRepository = new Mock<IRepository<User, int>>();
var userResolverService = new Mock<IUserResolverService>();
var tokenService = new Mock<ITokenService>();
var user = new User() {
Email = "test#test.com",
CustomerId = 1
};
var users = new List<User>() { user };
usersRepository.Setup(_ => _.GetAllAsync()).ReturnsAsync(users);
usersRepository.Setup(_ => _.AddAsync(It.IsAny<User>()))
.Returns<User>(arg => Task.FromResult(arg)) //<-- returning the input value from task.
.Callback<User>(arg => users.Add(arg)); //<-- use call back to perform function
userResolverService.Setup(_ => _.GetCustomerId()).Returns(2);
var accountService = new AccountService(usersRepository.Object, userResolverService.Object, tokenService.Object);
//act
var actual = await accountService.CreateUser(new UserDto {
Email = "email#example.com",
Password = "monkey123",
//...other code removed for brevity
});
//assert
Assert.IsTrue(actual);
Assert.IsTrue(users.Count == 2);
Assert.IsTrue(users.Any(u => u.CustomerId == 2);
}
Read up on Moq Quickstart to get a better understanding of how to use the mocking framework.
#sziszu As I understand correctly you can setup mock for unit test of your your public method. here is solution.
I have rewrite your code and my project structure is something look like visual studio project structure
Here is my code snippets
UserOperation Class
public class UserOperation
{
#region Fields
private readonly IUserResolverService _userResolverService;
private readonly IUsersRepository _usersRepository;
#endregion
#region Constructor
public UserOperation(IUserResolverService userResolverService, IUsersRepository usersRepository)
{
_userResolverService = userResolverService;
_usersRepository = usersRepository;
}
#endregion
#region Public Methods
public async Task<bool> CreateUser(UserDTO newUser)
{
newUser.CustomerId = _userResolverService.GetCustomerId();
if (await CheckUserExists(newUser)) return false;
var salt = GenerateSalt(10);
var passwordHash = GenerateHash(newUser.Password, salt);
await _usersRepository.AddAsync(new User
{
Role = newUser.Role,
CretedOn = DateTime.Now,
CustomerId = newUser.CustomerId,
Email = newUser.Email,
FirstName = newUser.FirstName,
LastName = newUser.LastName,
PasswordHash = passwordHash,
Salt = salt,
UpdatedOn = DateTime.Now
});
return true;
}
#endregion
#region PrivateMethods
private async Task<bool> CheckUserExists(UserDTO user)
{
var users = await _usersRepository.GetAllAsync();
var userCount = users.Count(u => u.Email == user.Email);
return userCount > 0;
}
private static string GenerateHash(string input, string salt)
{
var bytes = Encoding.UTF8.GetBytes(input + salt);
var sha256 = SHA256.Create();
var hash = sha256.ComputeHash(bytes);
return ByteArrayToString(hash);
}
private static string GenerateSalt(int size)
{
var rng = RandomNumberGenerator.Create();
var buff = new byte[size];
rng.GetBytes(buff);
return Convert.ToBase64String(buff);
}
private static string ByteArrayToString(byte[] ba)
{
var hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
#endregion
}
Below are my repositories and services.
public interface IUsersRepository
{
Task AddAsync(User user);
Task<ICollection<User>> GetAllAsync();
}
public class UsersRepository : IUsersRepository
{
public Task AddAsync(User user)
{
//Code for adding user
}
public Task<ICollection<User>> GetAllAsync()
{
//Code for get all user from DB
}
}
public interface IUserResolverService
{
string GetCustomerId();
}
public class UserResolverService : IUserResolverService
{
public string GetCustomerId()
{
//Code for Getting customerID.
}
}
Here You can test using mock
public class UserOperationTest
{
private readonly UserOperation _sut;
private readonly IUserResolverService _userResolverService;
private readonly IUsersRepository _userRepository;
public UserOperationTest()
{
_userResolverService = Substitute.For<IUserResolverService>();
_userRepository = Substitute.For<IUsersRepository>();
_sut = new UserOperation(_userResolverService, _userRepository);
}
[Fact]
public void CreateUser_SuccessWithMock()
{
// Arrange
var userDto = new UserDTO
{
Email = "ThirdUserUserEmail.Com"
};
var userList = new List<User>()
{
new User{CustomerId = "1", Email = "FirstUserEmail.Com"},
new User{CustomerId = "2", Email = "SecondUserEmail.Com"}
};
_userResolverService.GetCustomerId().Returns("3");
_userRepository.GetAllAsync().Returns(userList);
_userRepository.When(x => x.AddAsync(Arg.Any<User>())).Do(x =>
{
userList.Add(new User {Email = userDto.Email, CustomerId = userDto.CustomerId});
});
//Act
var result = _sut.CreateUser(userDto);
// Assert
result.Result.Should().BeTrue();
_userResolverService.Received(1).GetCustomerId();
_userRepository.Received(1).GetAllAsync();
_userRepository.Received(1).AddAsync(Arg.Any<User>());
userList.Count.Should().Be(3);
}
}

Unit test method mocking IList

I'm trying to mock the following method TryGetApns:
private readonly Func<string, ICommunicationClient> _communicationFactory;
public CommunicationApiFacade(Func<string, ICommunicationClient> communicationFactory)
{
_communicationFactory = communicationFactory;
}
public IList<ApnResponse> TryGetApns(string tenant)
{
GetApnsResponse response = null;
try
{
var communicationApiClient = _communicationFactory(tenant);
response = communicationApiClient.JctConfigurationService.GetApns();
}
catch (HttpException e)
{
...
}
return response?.Apns ?? new List<ApnResponse>();
}
with the following test:
private Mock<ICommunicationApiFacade> _communicationApiFacade;
[SetUp]
public void SetUp()
{
_fixture = new Fixture()
.Customize(new AutoMoqCustomization());
_communicationApiFacade = _fixture.Freeze<Mock<ICommunicationApiFacade>>();
}
[Test]
public void RunJctTests_WhenJctIsInAPrivateNetwork_ShouldReturnAPassedTest()
{
// Arrange
var jctApn = _fixture.Create<string>();
var message = _fixture.Build<JctLoggedIn>()
.With(x => x.ApnFromDevice, jctApn)
.Create();
var response = _fixture.Build<ApnResponse>()
.With(x => x.IsPrivateApn, true)
.With(x => x.ApnName, jctApn).Create();
_communicationApiFacade.Setup(x => x.TryGetApns(string.Empty))
.Returns(new List<ApnResponse> { response });
var subject = _fixture.Create<NetworkProviderTestRunner>();
// Act
var result = subject.Execute(message);
// Assert
Assert.That(result.Result, Is.True);
}
and this is the NetworkProviderTestRunner class:
private readonly ICommunicationApiFacade _communicationApi;
public NetworkProviderTestRunner(ICommunicationApiFacade communicationApi)
{
_communicationApi = communicationApi;
}
public JctTest Execute(JctLoggedIn message)
{
var apns = _communicationApi.TryGetApns(message.Tenant);
var jctApn = apns.FirstOrDefault(x => x.ApnName == message.ApnFromDevice);
if (jctApn != null)
{
var privateApn = apns.FirstOrDefault(x => x.PublicApnId.Equals(jctApn.Id));
if (privateApn != null || jctApn.IsPrivateApn)
return new JctTest { Result = true };
}
return new JctTest { Result = false };
}
JctLoggedIn class:
public class JctLoggedIn : Message
{
public string Imei { get; set; }
public string SimCardIdFromDevice { get; set; }
public string SimCardIdFromStorage { get; set; }
public string ApnFromDevice { get; set; }
public string ApnFromStorage { get; set; }
public string FirmwareFromDevice { get; set; }
public int DeviceTypeFromStorage { get; set; }
public int SerialNumber { get; set; }
}
but for some reason I always get back an empty list. I've tried to populate the list in the SetUp and also defining the output there but I's always the same. Any help?
While you could explicitly omit the Tenant property when building the message object, you could also change the Mock setup to this:
_communicationApiFacade.Setup(x => x.TryGetApns(message.Tenant))
.Returns(new List<ApnResponse> { response });
This line
_communicationApiFacade.Setup(x => x.TryGetApns(string.Empty))
.Returns(new List<ApnResponse> { response });
is telling the mock to return the list containing response when TryGetApns is called with an empty string for the tenant parameter.
What is the value of message.Tenant after message is created by this line:
var message = _fixture.Build<JctLoggedIn>()
.With(x => x.ApnFromDevice, jctApn)
.Create();
If it is not string.empty, your mock will not return the reponse list.
Replace
var response = _fixture.Build<ApnResponse>()
.With(x => x.IsPrivateApn, true)
.With(x => x.ApnName, jctApn).Create();
with
var response = _fixture.Build<ApnResponse>()
.With(x => x.Tenant, String.Empty)
.With(x => x.IsPrivateApn, true)
.With(x => x.ApnName, jctApn).Create();
if Tenant is writable.
If the property is writable, AutoFixture will create an object with non-empty value for Tenant without explicitly setting up what you want.

Categories

Resources