I have IDataService that contains generic crud operations
public interface IDataService<T>
{
Task<IEnumerable<T>> GetAll();
Task<IEnumerable<T>> GetAll(string[] includes = null);
Task<T> GetById(int id);
Task<T> Create(T entity);
Task<T> Update(int id, T entity);
Task<bool> Delete(int id);
}
and I have class GenericDataService<T> that implements the IDataService interface:
public class GenericDataService<T> : IDataService<T> where T : DomainObject
{
private readonly DeployToolDBContexFactory _contexFactory;
public GenericDataService(DeployToolDBContexFactory contexFactory)
{
_contexFactory = contexFactory;
}
public async Task<T> Create(T entity)
{
using (DeployToolDBContex contex = _contexFactory.CreateDbContext())
{
EntityEntry<T> createdResult = await contex.Set<T>().AddAsync(entity);
await contex.SaveChangesAsync();
return createdResult.Entity;
}
}
public async Task<bool> Delete(int id)
{
using (DeployToolDBContex contex = _contexFactory.CreateDbContext())
{
T entity = await contex.Set<T>().FirstOrDefaultAsync((e) => e.Id == id);
contex.Set<T>().Remove(entity);
await contex.SaveChangesAsync();
return true;
}
}
public async Task<IEnumerable<T>> GetAll()
{
using (DeployToolDBContex contex = _contexFactory.CreateDbContext())
{
IEnumerable<T> entities = await contex.Set<T>().ToListAsync();
return entities;
}
}
public async Task<T> GetById(int id)
{
using (DeployToolDBContex contex = _contexFactory.CreateDbContext())
{
T entity = await contex.Set<T>().FirstOrDefaultAsync((e) => e.Id == id);
return entity;
}
}
public async Task<T> Update(int id, T entity)
{
using (DeployToolDBContex contex = _contexFactory.CreateDbContext())
{
entity.Id = id;
contex.Set<T>().Update(entity);
await contex.SaveChangesAsync();
return entity;
}
}
public async Task<IEnumerable<T>> GetAll(string[] includes = null)
{
using (DeployToolDBContex contex = _contexFactory.CreateDbContext())
{
var query = contex.Set<T>().AsQueryable();
foreach (var include in includes)
query = query.Include(include);
return query.ToList();
}
}
}
To create objects I'm using a data store class that executes operations with the DataService object:
public class DataStore
{
private static DataStore instance = null;
public static DataStore Instance
{
get
{
instance = instance == null ? new DataStore() : instance;
return instance;
}
}
public IDataService<User> userDataService;
public DataStore()
{
this.userDataService = new GenericDataService<User>(new DeployToolDBContexFactory());
}
}
For example, user creation :
private async void AddUser()
{
User user = new User()
{
UserName = UserNameTxt,
RoleId = RoleSelected.Id
};
await DataStore.Instance.userDataService.Create(user);
}
I'm new to Moq and I want to write unit tests, for example, test of user creation
[Test]
public async Task CreateUser()
{
Mock<DeployToolDBContexFactory> dbContextFactory = new Mock<DeployToolDBContexFactory>();
Mock<DeployToolDBContex> dbContextMock = new Mock<DeployToolDBContex>(dbContextFactory.Object);
var user = new User()
{
UserName = "testName"
};
var mock = new Mock<GenericDataService<User>>(new DeployToolDBContexFactory());
mock.Setup(m => m.Create(user)).ReturnsAsync(new User() { UserName = user.UserName}).Verifiable();
var service = new GenericDataService<User>(dbContextFactory.Object);
User u = await service.Create(user);
Assert.AreEqual(u.UserName , user.UserName);
}
I'm getting this error :
System.NotSupportedException : Unsupported expression: m => m.Create(user)
Non-overridable members (here: GenericDataService.Create) may not be used in setup / verification expressions.
I tried to set DbSet as virtual.
Thanks for any help.
It looks like you are a bit mixed up with mocking and what exactly you should be testing. Your GenericDataService is essentially a Generic Repository pattern. This is actually an anti-pattern for EF, but there is plenty to read up on why you shouldn't use it with EF... Repository patterns in general are a good thing for facilitating unit testing, however this is because they serve as a boundary to avoid needing to try and mock a DbContext and its DbSets.
Firstly, your Test is testing the wrong thing. The test should be testing whatever method that will be calling your AddUser method. Whether this is in a Controller or a Service, etc. That controller would have a dependency of IDataService<User> declared which we would be mocking in order to test the controller:
For the sake of argument I've made AddUser() a public method. In your case you should have a public action or method that calls AddUser and would set up a test for that method. You should also structure your code to avoid being dependent on module-level state. For instance an AddUser method should not be reliant on private/protected state, it should ultimately be getting parameters to do things like perform actions or modify state. (kept to a minimum)
So let's assume we want to test a method that should call the DataService Create method and Create is expected to have added the item to the DBContext and assigned an ID. The purpose of the unit test is not to assert what EF actually does, but rather what the code under test should do with the results:
[Test]
public void EnsureAddUserCreatesUser()
{
const string userName = "New User";
const int roleId = 4;
const int UserId = 101;
var mockUserDataService = new Mock<IDataService<User>>();
mockUserDataService.Setup(m => m.Create(It.IsAny<User>())).Callback(m => {m.UserId = userId;});
var testController = new UserController(mockUserDataService.Object);
var user = await testController.AddUser(userName, roleId);
Assert.IsNotNull(user);
Assert.AreEqual(userId, user.UserId);
Assert.AreEqual(userName, user.UserName);
Assert.AreEqual(roleId, user.RoleId);
mockUserDataService.Verify(m => m.Create(It.IsAny<User>()), Times.Once);
}
What a test like this does is set up a mock of our repository/data service telling it to expect its input parameter. Since our AddUser method will be creating a new user based on some values, we tell it to expect It.IsAny<User>() which says "expect a user". From there we can have the mock perform some basic actions as if the DbContext added our user successfully such as populating a PK. This example populates the PK using the Callback method which accepts whatever User was passed in then sets our known PK. The value itself does not matter since we aren't actually inserting data, we just want a known value to get back that we can assert that the mock was actually called. Normally the AddUser might also expect to check the resulting data and return something like a success state with an ID. In this example I had the AddUser return the User. Other tests you might want to assert the behaviour if the AddUser attempts to add a duplicate user. In these cases the Moq might Throw an exception or otherwise return a different result.
From there we have returned the User, so we just assert the values are what were expected, including the PK, and that our mocked method was actually called.
Ultimately when it comes to unit testing, the key points are:
Your DataService/Repository serves as the boundary for the tests. (What you mock)
Your tests test business logic above this boundary.
Your tests mock the boundary, capturing all expected calls to that boundary, and either return known state, take action on passed in state, or throw exceptions based on what behaviour you want to test.
Your tests can then assert the mocks to verify that methods that were expected to be called were called, and any methods that were not expected to be called were not called. (Times.None)
Related
I have implemented an abstract generic repository that defines a set of methods for performing CRUD operations. I am trying to test them with the help of the Unit of Work pattern in a derived class. I'm using Moq as a testing framework and MockQueryable to enable Async operations like FirstOrDefaultAsync, AddAsync, SaveChangesAsync etc. I am experiencing an error testing the Add method. Here is a dummy project I created to show how things are set up.
DbContext Class
public class MyDBContextClass : DbContext, IDbContext
{
public MyDBContextClass(DbContextOptions options) : base(options) { }
public DbSet<Students> Students { get; set; }
}
Abstract class
public abstract class MyGenericRepository<TEntity> where TEntity : class
{
private readonly IDbContext context;
public MyMyGenericRepository(IDbContext context){
this.context = context;
}
public virtual async Task<int> Add(TEntity Entity)
{
await context.Set<TEntity>().AddAsync(entity); // throws a NullReferenceException during test
await context.SaveChangesAsync();
}
}
Unit of Work Class
public class StudentUnitOfWork : MyGenericRepository<Student>
{
public class StudentUnitOfWork(IDBContext context) : base(context)
{
}
}
The test class
public class StudentUnitOfWorkTest
{
[Fact]
public async Task Add()
{
var students = new List<Student>
{
new Student{ Id = 1, Name = "John Doe"},
new Student{ Id = 2, Name = "Jane Doe"}
}
var mockSet = students.AsQueryable().BuildMockDbSet();
var mockContext = new Mock<IDbContext>();
mockContext.Setup(_ => _.Students).Returns(mockSet.Object);
var sut = new StudentUnitOfWork(mockContext.Object);
var newStudentObj = new Student
{
Name = "Justin Doe"
}
await sut.Add(newStudentObj); // throws a NullReferenceException
mockSet.Verify(_ => _.AddAsync(It.IsAny<Student>(), It.IsAny<CancellationToken>()), Times.Once());
mockContext.Verify(_ => _.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once());
}
}
Overriding and injecting the base Add method also fails
public override async Task Add(Student student)
{
await base.Add(category); // Still throws a NullReferenceException
}
The test passes when I override the Implementation of Add in the Unit of Work Class and Set the actual dbSet in this case Students.
public class StudentUnitOfWork : MyGenericRepository<Student>
{
public override async Task Add(Student student)
{
await context.Students.AddAsync(student); // Works
await context.SaveChangesAsync();
}
}
Am I missing anything or DbContext.Set only works in production and not tests.
You are not configuring the Set method in IDbContext mock to return anything in your setup code, hence it returns null (default) and an exception is thrown when calling AddAsync.
Update:
Here's the line with error, where you call Set method:
await context.Set<TEntity>().AddAsync(entity);
And Here's your setup code in test, where you skip handling that same Set method:
var mockSet = students.AsQueryable().BuildMockDbSet();
var mockContext = new Mock<IDbContext>();
mockContext.Setup(_ => _.Students).Returns(mockSet.Object);
And since you are not mocking Set method, it returns null when test runs. You obviously cannot call AddAsync method on null, therefore NullReferenceException is thrown.
You should solve it by adding something like that:
mockContext.Setup(_ => _.Set<Student>()).Returns(<DbSet mock object>);
On the other hand,
await context.Students.AddAsync(student);
works, because you have mocked Students property
I am implemented my business logic using repository pattern. I basically have Approve method in my controller . I am calling the service method ApproveUserChangeRequest
which in turn invokes GetUserChangeRequest and ApproveUserChangeRequest in the UnitofWork class. I would like to know if this is standard or better way of doing it
Please bare in mind in to test the service methods
UserConroller
[HttpPost]
[AllowAnonymous]
[Route("approve-change-request")]
public IActionResult ApproveUserChangeRequest([FromBody] ApproveUserChangeRequests approveUserChangeRequests)
{
if (!ModelState.IsValid)
{
return BadRequest(new ResponseModel()
{
ResponseMessages = new Dictionary<string, string[]>
{
{ "Errors", ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage).ToArray() }
}
});
}
var result = _userService.ApproveUserChangeRequest(approveUserChangeRequests);
var message = string.Empty;
if (result.Succeeded)
{
return Ok(new ResponseModel()
{
ResponseMessages = new Dictionary<string, string[]>
{
{ "Info", new string[] { $"True" } }
}
});
}
message = string.Join(";", result.Errors.Select(x => $"Code: {x.Code}. Description: {x.Description}"));
_logger.Error(new IdentityException($"Error approving user change requests. Message: {message}"));
return BadRequest();
}
UserService class
public IdentityResult ApproveUserChangeRequest(ApproveUserChangeRequests approveUserChangeRequests)
{
var userChangeRequest = _userUow.GetUserChangeRequest(approveUserChangeRequests.UserChangeRequestID);
IdentityResult result = _userUow.ApproveUserChangeRequest(userChangeRequest, approveUserChangeRequests.ApprovedByAuthUserId, approveUserChangeRequests.AuthApplicationName);
return result;
}
UnitofWork class (uow)
public UserChangeRequest GetUserChangeRequest(int userChangeRequestId)
{
return UserChangeRequestRepository.GetQueryable(x =>
x.Id == userChangeRequestId)
.FirstOrDefault();
}
public IdentityResult ApproveUserChangeRequest(UserChangeRequest userChangeRequest, int approvedByAuthUserId, string authApplicationName)
{
var idResult = IdentityResult.Success;
// Check if UserChangeRequest is still Pending
bool isUserChangeRequestPending = UserChangeRequestRepository.GetQueryable(x => x.Id == userChangeRequest.Id && x.ChangeStatus == "Pending").Any();
if (isUserChangeRequestPending && approvedByAuthUserId > 0)
{
// Inserting record in the UserChangeRequestApproval table
InsertUserChangeRequestApproval(userChangeRequest);
SaveContext();
//Updating the user details in IdentityDB, ClientCompanyContact and AuthUser tables
UpdateUserDetails(userChangeRequest, authApplicationName);
}
else
{
idResult = IdentityResult.Failed(new IdentityError { Description = "No userchange request to approve" });
}
return idResult;
}
It's important to only test each part of your application in isolation. When we test public IActionResult ApproveUserChangeRequest we only want to ensure that it's doing its own job correctly. Anything that it calls should be mocked, and tested separately.
For this you will need to create interfaces for your repository, and your UnitOfWork class. This will allow them to be mocked, and their behaviour simulated.
You should also allow these classes to be injected into the consuming class using Dependency Injection, for example:
private readonly IUserService _userService;
public MyController(IUserService userService)
{
_userService = userService;
}
public IActionResult ApproveUserChangeRequest([FromBody] ApproveUserChangeRequests approveUserChangeRequests)
{
// ... snip
// this now uses the instance that was provided by dependency injection
var result = _userService.ApproveUserChangeRequest(approveUserChangeRequests);
}
You would then be able to test your class/method, whilst mocking the behaviour of your user service. The following example uses Moq, but you could use another mocking framework.
public void ApproveUserChangeRequest_PassesApproveChangeRequestsModelToService()
{
// mock the user service
var userService = new Mock<IUserService>();
// provide the controller with the user service
var controller = new MyController(userService);
// create the model for the request
var model = new ApproveUserChangeRequests();
// test the method
controller.ApproveUserChangeRequest(model);
// make sure that userService.ApproveUserChangeRequest was called with the correct arguments
userService.Verify(u => u.ApproveUserChangeRequest(model));
}
In the unit tests for the Controller, you only need to check that ApproveUserChangeRequest is doing it's job correctly. That is
verifying your model
raising an error if the model isn't valid
calling the user service if it is valid
sending you the correct response if it's successful
logging correctly to your logger
responding with bad request if appropriate
These should all be checked for in separate unit tests.
You should also then write tests for your UserService, and your UnitOfWork class. Only write tests for things that those classes are responsible for. If you find that the class is responsible for too many things, refactor your class until it obeys the single responsibility principle, this will greatly aid your ability to test your code.
My WEB API project is using a Generic Repository that implements an interface like this:
public interface IGenericEFRepository<TEntity> where TEntity : class
{
Task<IEnumerable<TEntity>> Get();
Task<TEntity> Get(int id);
}
public class GenericEFRepository<TEntity> : IGenericEFRepository<TEntity>
where TEntity : class
{
private SqlDbContext _db;
public GenericEFRepository(SqlDbContext db)
{
_db = db;
}
public async Task<IEnumerable<TEntity>> Get()
{
return await Task.FromResult(_db.Set<TEntity>());
}
public async Task<TEntity> Get(int id)
{
var entity = await Task.FromResult(_db.Set<TEntity>().Find(new object[] { id }));
if (entity != null && includeRelatedEntities)
{
//Some Code
}
return entity;
}
}
Well now I want to test this service. for this I have used the following code:
public class CustomerControllerTest
{
CustomerController _controller;
ICustomerProvider _provider;
ICustomerInquiryMockRepository _repo;
public CustomerControllerTest()
{
_repo = new CustomerInquiryMockRepository();
_provider = new CustomerProvider(_repo);
_controller = new CustomerController(_provider);
}
[Fact]
public async Task Get_WhenCalled_ReturnsOkResult()
{
// Act
var okResult = await _controller.Get();
// Assert
Assert.IsType<OkObjectResult>(okResult);
}
[Fact]
public async Task GetById_UnknownCustomerIdPassed_ReturnsNotFoundResult()
{
// Act
var notFoundResult = await _controller.Get(4);
// Assert
Assert.IsType<NotFoundResult>(notFoundResult);
}
}
Which my tests are working fine by creating a fake non-generic service manually with mock data (In-Memory) like below, instead of using my real generic interface and it's implementation that uses my database as data-source:
public interface ICustomerInquiryMockRepository
{
Task<IEnumerable<CustomerDTO>> GetCustomers();
Task<CustomerDTO> GetCustomer(int customerId);
}
And it's implementation:
public class CustomerInquiryMockRepository : ICustomerInquiryMockRepository
{
public async Task<IEnumerable<CustomerDTO>> GetCustomers()
{
return await Task.FromResult(MockData.Current.Customers);
}
public async Task<CustomerDTO> GetCustomer(int CustomerId)
{
var Customer = await Task.FromResult(MockData.Current.Customers.FirstOrDefault(p => p.CustomerID.Equals(CustomerId)));
if (includeTransactions && Customer != null)
{
Customer.Transactions = MockData.Current.Transactions.Where(b => b.CustomerId.Equals(CustomerId)).ToList();
}
return Customer;
}
}
And the MockData.Current.Customers is just a simple fake (In-Memory) List of Customers. Long story short, the above tests are working fine, however I am feeling I have repeated my self a lot and so I have decided to use Moq library instead of creating fake service manually. For this purpose I have used Moq like this:
public class CustomerControllerTest
{
CustomerController _controller;
ICustomerProvider _provider;
//ICustomerInquiryMockRepository _repo;
Mock<ICustomerInquiryMockRepository> mockUserRepo;
public CustomerControllerTest()
{
mockUserRepo = new Mock<ICustomerInquiryMockRepository>();
//_repo = new CustomerInquiryMockRepository();
_provider = new CustomerProvider(mockUserRepo.Object);
_controller = new CustomerController(_provider);
}
[Fact]
public async Task Get_WhenCalled_ReturnsOkResult()
{
mockUserRepo.Setup(m => m.GetCustomers())
.Returns(Task.FromResult(MockData.Current.Customers.AsEnumerable()));
// Act
var okResult = await _controller.Get();
// Assert
Assert.IsType<OkObjectResult>(okResult);
}
[Fact]
public async Task GetById_UnknownCustomerIdPassed_ReturnsNotFoundResult()
{
//Arrange
I don't know how can I use Moq here and in the other parts of my tests
// Act
var notFoundResult = await _controller.Get(4);
// Assert
Assert.IsType<NotFoundResult>(notFoundResult);
}
Now my question is the Mock is working fine when I use it for Mocking the GetCustomers method because I simply paste the code from GetCustomers method in the CustomerInquiryMockRepository in the Returns method of the Mock object. However I don't really have any idea how can I use Mock for my other methods inside this Repository. Should I replace anything that I have in the Return method?
You can mock out your repository like so:
var mockUserRepo = new Mock<ICustomerInquiryMockRepository>();
mockUserRepo.Setup(x => x.GetCustomers())
.Returns(Task.FromResult(MockData.Current.Customers.AsEnumerable());
mockUserRepo.Setup(x => x.GetCustomer(It.IsAny<int>()))
.Returns(res => Task.FromResult(MockData.Current.Customers.ElementAt(res));
If you want to mock out specific values for GetCustomer, you can do:
mockUserRepo.Setup(x => x.GetCustomer(It.Is<int>(y => y == 4)))
.Returns(res => Task.FromResult(/* error value here */));
I think the key here is to use It.Is or It.IsAny based on how you want to mock out the object. Generally, you also want to mock out interfaces that are used in production code, instead of having production code depend on something with Mock or Test in the name. I would recommend against taking a production code dependency on something named ICustomerInquiryMockRepository, if that is indeed what you're doing and not just part of the MCVE you've provided.
Tests usually use mocking to test the workflow of an application at a high level, so you would usually want to mock out your services level, call a controller, and verify that the services were called as expected. For example:
// Production class sample
class ProductionController
{
public ProductionController(IService1 service1, IService2 service2) { }
public void ControllerMethod()
{
var service1Result = service1.Method();
service2.Method(service1Result);
}
}
// Test sample
// arrange
var expectedResult = new Service1Result();
var service1 = Mock.Of<IService1>(x => x.Method() == expectedResult);
var service2 = Mock.Of<IService2>(x => x.Method(It.Is<Service1Result>(y => y == expectedResult)));
var controller = new ProductionController(service1, service2);
// act
controller.ControllerMethod();
// assert
Mock.Get(service1).Verify(x => x.Method(), Times.Once);
Mock.Get(service2).Verify(x => x.Method(expectedResult), Times.Once);
As you can see from the example, you aren't checking the business logic of either of the services, you're just validating that the methods were called with the expected data. The test is built around verification of methods being called, not any particular branching logic.
Also, unrelated to your question, Moq also has a cool syntax you can use for simple mock setups:
var repo = Mock.Of<ICustomerInquiryMockRepository>(x =>
x.GetCustomers() == Task.FromResult(MockData.Current.Customers.AsEnumerable()));
You can use Mock.Get(repo) if you need to do additional setup on the repository. It's definitely worth checking out, I find it much nicer to read.
I'm having a difficult time trying to understand how to appropriately return mocked data from a simulated database call in a unit test.
Here's an example method I want to unit test (GetBuildings):
public class BuildingService : IBuildingService {
public IQueryable<Building> GetBuildings(int propertyId)
{
IQueryable<Building> buildings;
// Execution path for potential exception thrown
// if (...) throw new SpecialException();
// Another execution path...
// if (...) ...
using (var context = DataContext.Instance())
{
var Params = new List<SqlParameter>
{
new SqlParameter("#PropertyId", propertyId)
};
// I need to return mocked data here...
buildings = context
.ExecuteQuery<Building>(System.Data.CommandType.StoredProcedure, "dbo.Building_List", Params.ToArray<object>())
.AsQueryable();
}
return buildings;
}
}
So GetBuildings calls a stored procedure.
So I need to mock the DataContext, that of which I can override and set a testable instance. So what happens here is, in the above example DataContext.Instance() does return the mocked object.
[TestFixture]
public class BuildingServiceTests
{
private Mock<IDataContext> _mockDataContext;
[SetUp]
public void Setup() {
_mockDataContext = new Mock<IDataContext>();
}
[TearDown]
public void TearDown() {
...
}
[Test]
public void SomeTestName() {
_mockDataContext.Setup(r =>
r.ExecuteQuery<Building>(CommandType.StoredProcedure, "someSproc"))
.Returns(new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable());
DataContext.SetTestableInstance(_mockDataContext.Object);
var builings = BuildingService.GetBuildings(1, 1);
// Assert...
}
Please ignore some of the parameters, like propertyId. I've stripped those out and simplified this all. I simply can't get the ExecuteQuery method to return any data.
All other simple peta-poco type methods I can mock without issue (i.e. Get, Insert, Delete).
Update
DataContext.Instance returns the active instance of the DataContext class, if exists, and if not exists, returns a new one. So the method of test under question returns the mocked instance.
Do not mock DataContext. Because mocking DataContext will produce tests tightly coupled to the implementation details of DataContext. And you will be forced to change tests for every change in the code even behavior will remain same.
Instead introduce a "DataService" interface and mock it in the tests for BuildingService.
public interface IDataService
{
IEnumerable<Building> GetBuildings(int propertyId)
}
Then, you can tests implementation of IDataService agains real database as part of integration tests or tests it agains database in memory.
If you can test with "InMemory" database (EF Core or Sqlite) - then even better -> write tests for BuildingService against actual implementation of DataContext.
In tests you should mock only external resources (web service, file system or database) or only resources which makes tests slow.
Not mocking other dependencies will save you time and give freedom while you refactoring your codebase.
After update:
Based on the updated question, where BuildingService have some execution path - you can still testing BuildingService and abstract data related logic to the IDataService.
For example below is BuildingService class
public class BuildingService
{
private readonly IDataService _dataService;
public BuildingService(IDataService dataService)
{
_dataService = dataService;
}
public IEnumerable<Building> GetBuildings(int propertyId)
{
if (propertyId < 0)
{
throw new ArgumentException("Negative id not allowed");
}
if (propertyId == 0)
{
return Enumerable.Empty<Building>();
}
return _myDataService.GetBuildingsOfProperty(int propertyId);
}
}
In tests you will create a mock for IDataService and pass it to the constructor of BuildingService
var fakeDataService = new Mock<IDataContext>();
var serviceUnderTest = new BuildingService(fakeDataService);
Then you will have tests for:
"Should throw exception when property Id is negative"
"Should return empty collection when property Id equals zero"
"Should return collection of expected buildings when valid property Id is given"
For last test case you will mock IDataService to return expected building only when correct propertyId is given to _dataService.GetBuildingsOfProperty method
In order for the mock to return data is needs to be set up to behave as expected given a provided input.
currently in the method under test it is being called like this
buildings = context
.ExecuteQuery<Building>(System.Data.CommandType.StoredProcedure, "dbo.Building_List", Params.ToArray<object>())
.AsQueryable();
Yet in the test the mock context is being setup like
_mockDataContext.Setup(r =>
r.ExecuteQuery<Building>(CommandType.StoredProcedure, "someSproc"))
.Returns(new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable());
Note what the mock is told to expect as parameters.
The mock will only behave as expected when provided with those parameters. Otherwise it will return null.
Consider the following example of how the test can be exercised based on the code provided in the original question.
[Test]
public void SomeTestName() {
//Arrange
var expected = new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable();
_mockDataContext
.Setup(_ => _.ExecuteQuery<Building>(CommandType.StoredProcedure, It.IsAny<string>(), It.IsAny<object[]>()))
.Returns(expected);
DataContext.SetTestableInstance(_mockDataContext.Object);
var subject = new BuildingService();
//Act
var actual = subject.GetBuildings(1);
// Assert...
CollectionAssert.AreEquivalent(expected, actual);
}
That said, the current design of the system under test is tightly coupled to a static dependency which is a code smell and makes the current design follow some bad practices.
The static DataContext which is currently being used as a factory should be refactored as such,
public interface IDataContextFactory {
IDataContext CreateInstance();
}
and explicitly injected into dependent classes instead of calling the static factory method
public class BuildingService : IBuildingService {
private readonly IDataContextFactory factory;
public BuildingService(IDataContextFactory factory) {
this.factory = factory
}
public IQueryable<Building> GetBuildings(int propertyId) {
IQueryable<Building> buildings;
using (var context = factory.CreateInstance()) {
var Params = new List<SqlParameter> {
new SqlParameter("#PropertyId", propertyId)
};
buildings = context
.ExecuteQuery<Building>(System.Data.CommandType.StoredProcedure, "dbo.Building_List", Params.ToArray<object>())
.AsQueryable();
}
return buildings;
}
}
This will allow for a proper mock to be created in injected into the subject under test without using a static workaround hack.
[Test]
public void SomeTestName() {
//Arrange
var expected = new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable();
_mockDataContext
.Setup(_ => _.ExecuteQuery<Building>(CommandType.StoredProcedure, It.IsAny<string>(), It.IsAny<object[]>()))
.Returns(expected);
var factoryMock = new Mock<IDataContextFactory>();
factoryMock
.Setup(_ => _.CreateInstance())
.Returns(_mockDataContext.Object);
var subject = new BuildingService(factoryMock.Object);
//Act
var actual = subject.GetBuildings(1);
// Assert...
CollectionAssert.AreEquivalent(expected, actual);
}
I'm not familiar with mocking. I'd like test if my method GetById return me an object User with an Id. Below the code, I'd like test if the GetById(10) return me an User with id = 10.
I set the moq (I hope it's correct) but how execute the moq ?
Thanks,
[TestMethod]
public void MyMoq()
{
var userMock = new Mock<IUsers>();
userMock.Setup(x => x.GetById(10)).Returns(new User());
//After ?
new Users().GetById(10);
}
public interface IUsers
{
IUser GetById();
}
public IUser GetById(int id)
{
using (var context = ....)
{
//code here
//return user here
}
}
Alright, as I said in comments, it's not clear for me what code you're trying to test. I see two options here.
1) The Users class implements IUsers interface and your intention is to test implementation of GetById(int) method.
In such case you do NOT need to mock the 'Users#GetById(id)' method, you just need to call it and check the result.
The code should look similar to:
interface IUser
{
int Id { get; }
}
class User : IUser
{
public int Id { get;set; }
}
interface IUsers
{
IUser GetById(int id);
}
class Users : IUser
{
public IUser GetById(int id)
{
// TODO: make call db call
// TODO: parse the result
// TODO: and return new User instance with all the data from db
return new User{ Id = id };
}
}
[TestMethod]
public void MyMoq()
{
// TODO: prepare/mock database. That's whole another story.
var users = new Users();
// act
var user = users.GetById(10);
// assert
Assert.AreEqual(10, user.Id);
}
2) Your Users#GetById(int) method is supposed to call the IUsers#GetById(int) and return the result. In such case you need to create mock of IUsers(as you've shown in question) and pass it to Users. The code should be(sorry for possible duplication):
interface IUser
{
int Id { get; }
}
class User : IUser
{
public int Id { get;set; }
}
interface IUsers
{
IUser GetById(int id);
}
class Users : IUser
{
private readonly IUser _users;
public Users(IUser users)
{
_users = users;
}
public IUser GetById(int id)
{
// next line of code is to be tested in unit test
return _users.GetById(id);
}
}
[TestMethod]
public void MyMoq()
{
var usersMock = new Mock<IUsers>();
usersMock.Setup(x => x.GetById(10)).Returns(new User());
var users = new Users(usersMock.Object);
// act
var user = users.GetById(10);
// assert
Assert.AreEqual(10, user.Id);
}
p.s. Could be useful to take a look at moq tutorial and The Art of Unit Testing book, Part 2 - Core techniques(page 47) - stubs, mocks, etc.
I don't quite sure what you want to test. You also has Mock class with some methods that you don't describe.
However, answering your question about mocking. Consider this class:
public class MyMoq : IUsers
{
private readonly Mock<IUsers> userMock;
public MyMoq(Mock<IUsers> userMock){
this.userMock = userMock;
}
[TestMethod]
public IUser GetById()
{
userMock.Setup(x => x.GetById(10)).Returns(new User());
//After ?
return new UsersDb().GetById(10);
}
}
To use it:
MyMoq moq = new MyMoq(new Mock<IUsers>());
User u = moq.GetById();
My assumption in this example is Mock<IUsers> is a repository and MyMoq is a service class. Also, IUser is an Entity Interface, and IUsers is a service interface.
To test userMock.Object should return the actual mocked IUsers object.
var userMock = new Mock<IUsers>();
userMock.Setup(x => x.GetById(10)).Returns(new User());
var mockobject = userMock.Object;
//Returns your mocked new User() instance
var newUserObject = mockobject.GetById(10);
In the above code, newUserObject has only created a new instance of User.
To test your GetById method, you need to call it again and Assert.
Assert.AreEqual(newUserObject.GetById(20).ID, 20); //Assume User has a property ID
Suggestion: It should be possible to have a better way to create the User instance.
GetById should be in it's own class (probably called Users) if that is what is being tested. Users could then accept a context in the constructor. Then mock this context to return stubbed data.
So in summary
Class Users implementing IUsers
Users has a constructor with parameter IContext (or whatever it would be called here)
Test class would mock IContext but not IUsers.
Call users.GetById and check the output is correct.
The way you would set up your context depends on what type of context it is, but see https://cuttingedge.it/blogs/steven/pivot/entry.php?id=84.