I am new to unit testing and I am trying to write a test to verify that when I update my user object the correct fields are being updated. My unit test looks like:
[Test]
public void ShouldUpdateExistingEmployee()
{
var employees = new Employee[]
{
new Employee()
{
EmployeeId = 1,
FirstName = "Johhn",
LastName = "Smiths",
Email = "John.Smith1#Illinois.gov",
IsActive = true
}
};
var mockContext = new Mock<SqlContext>();
mockContext.Setup(e => e.Employees).ReturnsDbSet(employees);
mockContext.Setup(m => m.Employees.Find(It.IsAny<object[]>()))
.Returns<object[]>(
ids => employees.FirstOrDefault(d => d.EmployeeId == (int)ids[0]));
var sut = new EmployeeRepository(mockContext.Object);
var employeeToUpdate = new Employee
{
EmployeeId = 1,
FirstName = "John",
LastName = "Smith",
Email = "John.Smith#Illinois.gov",
IsActive = true
};
sut.Save(employeeToUpdate);
Assert.That(employees.First().FirstName, Is.EqualTo(employeeToUpdate.FirstName));
Assert.That(employees.First().LastName, Is.EqualTo(employeeToUpdate.LastName));
Assert.That(employees.First().Email, Is.EqualTo(employeeToUpdate.Email));
}
My repository looks like:
public void Save(Employee employee)
{
if (employee.EmployeeId > 0)
{
Employee dbEmployee = Context.Employees.Find(employee.EmployeeId);
Context.Entry(dbEmployee).CurrentValues.SetValues(employee);
}
else
{
Context.Employees.Add(employee);
}
Context.SaveChanges();
}
The problem is when I get to Context.Entry(dbEmployee).CurrentValues.SetValues(employee); in my repository I get the following error: Member 'CurrentValues' cannot be called for the entity of type 'Employee' because the entity does not exist in the context. To add an entity to the context call the Add or Attach method of DbSet<Employee>.
Any help would be appreciated!
Based on this article, you should change:
Employee dbEmployee = Context.Employees.Find(employee.EmployeeId);
Context.Entry(dbEmployee).CurrentValues.SetValues(employee);
to:
Context.Employees.Attach(employee)
Then you should change your assert to verify that the Attach method was called with employeeToUpdate.(you hide the DBSet<> in the method ReturnsDbSet so I couldn't add an example...)
One more thing, I think you should take a look in this code snippet which shows the right way to mock DBContext and DBSet<> using Moq. Or read this article
Related
I am writing simple linq query as below. The first select mapper works fine. But the second mapper class not working and throwing 'Value cannot be null; Parameter name: source' This is really driving me crazy. Don't know how to debug as pressing 'F11' not going into the method. In the following 'User' class is the data object.
main method which returns the result:
return await query
.Select(UserSearchMappingSelector.ToDto(hasSensitiveAccess))
.Select(UserSearchMappingSelector.ToViewModelFromDto(hasSensitiveAccess))
.OrderBy(user => user.Name)
.ToListAsync();
public static Expression<Func<User, SearchPersonDto>> ToDto(bool hasSensitiveAccess)
{
return (User entity)
=> new SearchPersonDto
{
Id = entity.Id,
UserGuid = entity.UserGuid,
KnownAs = entity.KnownAs,
Forename = entity.Forename,
Surname = entity.Surname,
Manager = GetManager(entity),
Subordinates = GetSubordinates(entity, hasSensitiveAccess)
};
}
public static Expression<Func<SearchPersonDto, SearchPerson>> ToViewModelFromDto(bool hasSensitiveAccess)
{
return (SearchPersonDto entity)
=> new SearchPerson
{
Id = entity.Id,
UserGuid = entity.UserGuid,
Name = User.GetDisplayName(entity.KnownAs, entity.Forename, entity.Surname),
Manager = new Manager() { Id = entity.Manager.Id },
Subordinates = new List<Subordinate>()
};
}
public partial class User
{
// Logic is in sync with [org].[Select_AuditUsers]
// if KnownAs is empty string DisplayName should be " surname"
[NotMapped]
public string DisplayName => GetDisplayName(KnownAs, Forename, Surname);
public static string GetDisplayName(string knownAs , string forename, string surname)
{
return $"{knownAs ?? forename} {surname}";
}
}
Not sure why the second mapper not able to read the manager property from the SearchPersonDto class.
Can anyone please help me what the issue with the above query?
Also if i comment the Manager and Subordinates properties in second mapper, everything is working fine.
Thanks
I have some problem when I setup my mock for add entity. In case when I want get entity/entites my mock-setup is working fine, but when I want create(add). I setup method for create and it returned null in result.
P.S AddProductAsync in my suspicion, this method may not work, although I checked in the debug, there is a call to the method
public class ProductServiceTests
{
private Mock<IProductRepository> _productMockRepo = new Mock<IProductRepository>();
private ProductService _sut;
public ProductServiceTests()
{
_sut = new ProductService(_productMockRepo.Object);
}
It's my test method
[Fact]
public async Task AddProduct_ShouldReturnProduct_WhenInputDataIsCorrect()
{
//Arrange
var productId = Guid.NewGuid().ToString();
var actualProduct = new ProductModel
{
Name = "test",
Price = 1,
Category = Category.Foods,
Quantity = 2
};
var addingProduct = new Product
{
Name = actualProduct.Name,
Price = actualProduct.Price,
Category = actualProduct.Category,
Quantity = actualProduct.Quantity
};
_productMockRepo.Setup(x => x.AddProductAsync(addingProduct))
.ReturnsAsync(addingProduct);
//Act
var result = await _sut.AddProductAsync(actualProduct);
//Assert
Assert.Equal(actualProduct.Name, result.Name);
_productMockRepo.Verify(x => x.AddProductAsync(addingProduct), Times.Once);
}
}
_sut is my service and _productMockRepo is my mock repository
For testing I use NuGet packadge "Moq"
Thanks)
The issue is that the expectation was setup to use addingProduct, a specific instance
_productMockRepo.Setup(x => x.AddProductAsync(addingProduct))
.ReturnsAsync(addingProduct);
but when exercising the test it is getting another instance created within the member under test
var result = await _sut.AddProductAsync(actualProduct);
which I can only assume does something similar to what was done in the here
var addingProduct = new Product {
Name = actualProduct.Name,
Price = actualProduct.Price,
Category = actualProduct.Category,
Quantity = actualProduct.Quantity
};
since the subject under test (ie: ProductService.AddProductAsync(Product product)) is not shown.
Because it is not the actual instance used in the setup the mock will return null by default.
In this case, loosen the argument match during setup of expected behavior
//...
_productMockRepo
.Setup(x => x.AddProductAsync(It.IsAny<Product>())) //<-- loosen expected match
.ReturnsAsync((Product p) => p); //<-- return the argument that was passed to the member
//...
and change the expression for the verification to inspect what was passed
_productMockRepo.Verify(x => x.AddProductAsync(It.Is<Product>(p => addingProduct.Name == p.Name && ... )), Times.Once);
Reference: MOQ Quickstart - Matching Arguments
I have been tasked at work to create a test script that will (using entity framework) look-up a value in a table if existing.
The code I have to work with has this constructor:
public PostProductHelper(
Func<IMachineDBContext> contextFactory )
{
_contextFactory = contextFactory;
}
My method to unit test could be something like this:
public string CheckAndRemoveProductNameFileExtIfExists(
string productName )
{
using ( var ctx = CreateContext() )
{
return ctx.Products.FirstOrDefault( d => d.Name == productName);
}
}
so, going by the examples when Googling I am supposed to do this:
MockProductRepository = Substitute.For<IProductRepository>();
MockMessagePublicationService = Substitute.For<IMessagePublicationService>();
MockMachineDBContext = Substitute.For<IMachineDBContext>(););
var Products = new List<Product>
{
new Product { Name = "BBB" },
new Product { Name = "ZZZ" },
new Product { Name = "AAA" },
}.AsQueryable();
MockMachineDBContext.Products.AddRange( Products );
But in order to pass to my constructor I have to modify this to:
MockProductRepository = Substitute.For<IProductRepository>();
MockMessagePublicationService = Substitute.For<IMessagePublicationService>();
MockMachineDBContext = Substitute.For<Func<IMachineDBContext>>();
var Products = new List<Product>
{
new Product { Name = "BBB" },
new Product { Name = "ZZZ" },
new Product { Name = "AAA" },
}.AsQueryable();
MockMachineDBContext.Products.AddRange( Products );
which errors on the last line saying 'cannot resolve symbol 'Products'.
I am not allowed to change this constructor and I appreciate I may be making some mistakes.
You are missing () after MockMachineDBContext in MockMachineDBContext().Products.AddRange( Products );
MockMachineDBContext is delegate.
For usage also see Substituting for delegates in NSubstitute.
I am trying to Test a function which I have below to get all active categories which will use a relation predicate bucket. The mocked data in the initializer class adds three objects which are both active and not deleted. It adds a fourth to the end which is deleted and not active.
During the test, the call will return all four objects, and not the expected number of three. This is where I am stuck with.
I have tried making _randomCategories as a Queryable object, but this also failed.
There is a lot of code in the first class so it may be hard to follow, so each part is broken into regions saying which part it performs, i.e. Test Setup, Mock Data, & the Tests themselves.
The Mock Data region are the expected results. May not be necessary for my needs here as it is not used in this other than to get the expected count, but it may be relevant to the overall structure of the tests.
CategoryServiceTests.cs
#region Test Setup
public class CategoryServiceFixture : IDisposable
{
public CategoryService Sut { get; private set; }
private SystemRepository SystemRepository { get; set; }
private Mock<CategoryRepository> _categoryRepositoryMock;
private List<CategoryEntity> _randomCategories;
public CategoryServiceFixture()
{
// Init Category List
_randomCategories = CategoryEntityInitializer.GetAllMockCategories();
// Init repository
_categoryRepositoryMock = new Mock<CategoryRepository>(new object[] { null });
// Setup mocking behavior
// BaseRepository
_categoryRepositoryMock
.Setup(m => m.GetAll(It.IsAny<IRelationPredicateBucket>(), It.IsAny<IDataAccessAdapter>()))
.Returns(_randomCategories);
SystemRepository = new SystemRepository(category: _categoryRepositoryMock.Object);
Sut = new CategoryService(this.SystemRepository);
}
public void Dispose()
{
//Sut.Dispose();
}
}
[CollectionDefinition("CategoryService Collection")]
public class CategoryServiceCollection : ICollectionFixture<CategoryServiceFixture> { }
#endregion
#region Mock Data
public static class CategoryRepositoryMockData
{
public static IEnumerable<object> GetCategories
{
get
{
yield return new object[] { 1, new List<CategoryEntity>() {
new CategoryEntity
{
CategoryId = 1,
Name = "Test1",
IsDeleted = false,
IsActive = true
},
new CategoryEntity
{
CategoryId = 2,
Name = "Test2",
IsDeleted = false,
IsActive = true
},
new CategoryEntity
{
CategoryId = 3,
Name = "Test3",
IsDeleted = false,
IsActive = true
}
}};
}
}
}
#endregion
#region Tests
[Collection("CategoryService Collection")]
public class CategoryServiceTests
{
private CategoryServiceFixture _fixture;
public CategoryServiceTests(CategoryServiceFixture fixture)
{
_fixture = fixture;
}
[Theory]
[Trait("Category", "Get All Active Categories")]
[Trait("Expected", "Return Correct")]
[MemberData("GetCategories", MemberType = typeof(CategoryRepositoryMockData))]
public void GetActiveCategories_ShouldReturn(int id, IList<CategoryEntity> expectedCategoryObjects)
{
var result = _fixture.Sut.GetActiveCategories();
Assert.Equal(expectedCategoryObjects.Count, result.Count);
}
}
#endregion
This class generates the mock database objects. This is what is supposed to be searched through and select the correct ones from the list.
CategoryEntityInitializer.cs
public static class CategoryEntityInitializer
{
public static List<CategoryEntity> GetAllMockCategories()
{
List<CategoryEntity> _categories = new List<CategoryEntity>();
for (var i = 1; i <= 3; i++)
{
var entity = new CategoryEntity()
{
CategoryId = i,
Name = String.Format("{0}{1}", "Test", i),
IsDeleted = false,
IsActive = true
};
_categories.Add(entity);
}
var lastEntity = new CategoryEntity()
{
CategoryId = 4,
Name = String.Format("{0}{1}", "Test", 4),
IsDeleted = true,
IsActive = false
};
_categories.Add(lastEntity);
return _categories;
}
}
This class is where the predicate is.
CategoryService.cs
public class CategoryService : BaseService
{
public IList<CategoryModel> GetActiveCategories()
{
var bucket = new RelationPredicateBucket();
bucket.PredicateExpression.Add(CategoryFields.IsDeleted == false);
bucket.PredicateExpression.Add(CategoryFields.IsActive == true);
var categoriesEntities = _systemRepository.Category.GetAll(bucket);
return CategoryMapper.MapToModels(categoriesEntities);
}
}
The rest of the code structure works fine for every other test and across different test classes. This is the first time I have had to test the relation predicate bucket.
UPDATE 18/05/16
I found the solution to the problem. Answer in the below code.
_categoryRepositoryMock
.Setup(m => m.GetAll(It.IsAny<IRelationPredicateBucket>(), It.IsAny<IDataAccessAdapter>()))
.Returns(new Func<IRelationPredicateBucket, IDataAccessAdapter, IEnumerable<CategoryEntity>>(
(bucket, adapter) => _randomCategories.Where(a => a.IsDeleted == false && a.IsActive == true)));
Old Answer 11/05/16
I believe I have found part of the answer. In mocking the repository, I was returning all four objects, no matter if the predicate bucket would work or not.
_categoryRepositoryMock
.Setup(m => m.GetAll(It.IsAny<IRelationPredicateBucket>(), It.IsAny<IDataAccessAdapter>()))
.Returns(_randomCategories);
This will cause it to return all four as I have not implemented the bucket to filter out the matches. I thought that the .Returns would place the data into the repository as if returned from the database and then get filtered with the bucket.
What I have found out is that the call to the repository does not go to the one it would if it was running normally, but instead goes to the mocked repository and this is where you need to filter the data to be returned.
It needs to be done something similar to this.
_categoryRepositoryMock
.Setup(m => m.GetAll(It.IsAny<IRelationPredicateBucket>(), It.IsAny<IDataAccessAdapter>()))
.Returns(
(IRelationPredicateBucket bucket) => _randomCategories.Where(x => x.Name.Equals(bucket)));
Although this still give me a problem as I don't know how to get inside the bucket to match to the list, it is at least heading in the correct direction.
NOTE: I have also found from searching that another reason for this type of failure is due to the code, in this case the function being called is too complex to be tested. It should be seperated into smaller chucks to make it less complex and to test each part seperately.
Before
public class CategoryService : BaseService
{
public IList<CategoryModel> GetActiveCategories()
{
var bucket = new RelationPredicateBucket();
bucket.PredicateExpression.Add(CategoryFields.IsDeleted == false);
bucket.PredicateExpression.Add(CategoryFields.IsActive == true);
var categoriesEntities = _systemRepository.Category.GetAll(bucket);
return CategoryMapper.MapToModels(categoriesEntities);
}
}
After
public class CategoryService : BaseService
{
public IList<CategoryModel> GetActiveCategories()
{
var bucket = GetActiveCategoriesBucket();
var categoriesEntities = _systemRepository.Category.GetAll(bucket);
return CategoryMapper.MapToModels(categoriesEntities);
}
public RelationPredicateBucket GetActiveCategoriesBucket()
{
var bucket = new RelationPredicateBucket();
bucket.PredicateExpression.Add(CategoryFields.IsDeleted == false);
bucket.PredicateExpression.Add(CategoryFields.IsActive == true);
return bucket;
}
}
I'm using xUnit to test my project. I have a test that checks if a user has been added the a list of users shown below:
private readonly IJsonService _jsonService;
private readonly IUserService _userService;
[Fact]
public void Add_User_To_User_List()
{
//Given
_userService = new UserService(_jsonService, _guidService);
_jsonService = Substitute.For<IJsonService>();
var _fakeUserJsonFile = "Users.json";
var _fakeNewUser = new User()
{
ID = new Guid(),
FirstName = "Denis",
LastName = "Menis"
};
var _fakeUserList = new List<User>
{
new User()
{
ID = new Guid(),
FirstName = "Paddy",
LastName = "Halle"
},
new User()
{
ID = new Guid(),
FirstName = "Job",
LastName = "Blogs"
}
};
var _fakeUpdatedUserList = new List<User>
{
new User()
{
ID = new Guid(),
FirstName = "Paddy",
LastName = "Halle"
},
new User()
{
ID = new Guid(),
FirstName = "Job",
LastName = "Blogs"
},
new User()
{
ID = new Guid(),
FirstName = "Denis",
LastName = "Menis"
}
};
_jsonService.DeserializeObject<User>(_fakeUserJsonFile).Returns(_fakeUserList);
_jsonService.SerializeObject(_fakeUserJsonFile, _fakeUpdatedUserList).Returns(true);
//When
var result = _userService.AddUser(_fakeNewUser);
//Then
Assert.Contains(_fakeNewUser, _fakeUpdatedUserList);
}
Now I know that the code works because I wrote it first but when I run my test it fails! Below is my code to add the user to the user list:
public bool AddUser(User user)
{
var userList = GetUsers();
user.ID = _guidService.NewGuid();
userList.Add(user);
var serializeObject = _jsonService.SerializeObject(_fileName, userList);
return serializeObject;
}
GetUser Method:
public List<User> GetUsers()
{
return _jsonService.DeserializeObject<User>(_fileName).ToList();
}
Deserialise Method:
private readonly IFileSystem _file;
private readonly HttpContextBase _httpContext;
private readonly ILogger _logger;
public JsonService(IFileSystem file, HttpContextBase httpContext, ILogger logger)
{
_file = file;
_httpContext = httpContext;
_logger = logger;
}
public IEnumerable<T> DeserializeObject<T>(string fileName)
{
try
{
var relativeFileName = _httpContext.Server.MapPath(fileName);
var readFile = _file.ReadAllText(relativeFileName);
var list = JsonConvert.DeserializeObject<List<T>>(readFile);
return list;
}
catch (Exception ex)
{
_logger.LogException(ex);
return null;
}
}
FileSystem class:
public class FileSystem : IFileSystem
{
public void WriteAllText(string path, string contents)
{
File.WriteAllText(path, contents);
}
public string ReadAllText(string path)
{
return File.ReadAllText(path);
}
}
When I run my test, var serializeObject = _jsonService.SerializeObject(_fileName, userList); from the AddUser method returns false every time.
I think it's doing this because even though it's the same data as the expected result, in memory it's a different reference to the same data.
Can anyone help me with this I want it to return the same referenced data. If I'm not being very clear I can elaborate more. Thanks
You're absolutely right that the problem is rooted in the fact that _fakeUserList and _fakeUpdatedUserList reference two completely different objects. You've configured _jsonService.SerializeObject to return true when it is passed a reference to _fakeUpdatedUserList - but you're actually passing a reference to (a modified) _fakeUserList.
Basically, _fakeUpdatedUserList is completely unnecessary. You can focus on _fakeUserList, since that is the object that gets provided to the SUT (via DeserializeObject<User>, presumably).
For example:
[Fact]
public void Add_User_To_User_List()
{
//Given
_userService = new UserService(_jsonService, _guidService);
_jsonService = Substitute.For<IJsonService>();
var _fakeUserJsonFile = "Users.json";
var _fakeNewUser = new User()
{
ID = new Guid(),
FirstName = "Denis",
LastName = "Menis"
};
var _fakeUserList = new List<User>
{
new User()
{
ID = new Guid(),
FirstName = "Paddy",
LastName = "Halle"
},
new User()
{
ID = new Guid(),
FirstName = "Job",
LastName = "Blogs"
}
};
_jsonService.DeserializeObject<User>(_fakeUserJsonFile).Returns(_fakeUserList);
_jsonService.SerializeObject(_fakeUserJsonFile, _fakeUserList).Returns(true); // Match the original _fakeUserList, since that is what gets passed in by the implementation
//When
var result = _userService.AddUser(_fakeNewUser);
//Then
Assert.Contains(_fakeNewUser, _fakeUserList); // Verify that the provided _fakeUserList has been modified
}
On a side note: you can actually remove a lot of the details from this test, as they're irrelevant to the functionality that is being tested. For example, _fakeUserList can initially be empty - it doesn't have to contain any dummy values. And you can use the default values for _fakeNewUser (i.e., without specifying FirstName etc.), as they're not referenced at all in this test.
Edit: Thanks for posting the additional code (for GetUsers, etc.). This code shows that you're invoking ToList() on the IEnumerable<User> that is returned by DeserializeObject<User>. This is why your mock object is not behaving as you expect: the list returned by ToList() is a completely separate list from _fakeUserList.
Additionally, I don't see anywhere in your test where _fakeUserJsonFile is being injected into the SUT. So the _fileName in AddUser might not be what you're expecting, as well.
To get around this, you either need to modify your design (e.g., to not call ToList), or modify the expected behavior in your test. While you may want to consider the possibility of the former, the latter might be easier.
For example:
// Match any filename (unless you have a way of getting _fakeUserJsonFile into the SUT)
// Match any list, as long as it contains the new user
_jsonService.SerializeObject(Arg.Any<string>(), Arg.Is<List<User>>(list => list.Contains(_fakeNewUser))).Returns(true);
//When
var result = _userService.AddUser(_fakeNewUser);
//Then
Assert.IsTrue(result); // Only returns true if the mock object is invoked as expected
// There is no way to verify the following assertion, because the test has no way of accessing the "updated" list
//Assert.Contains(_fakeNewUser, _fakeUserList);