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.
Related
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)
I have a code like this (I have to test a repo, you'll see the code below)
public class SomeClass
{
public AsyncPolicyWrap PropName { get; }
public SomeClass(...)
{
PropName = Policy.WrapAsync(someRetry,someCircuitBreaker)
// here there are passed some methods that return someRetry - AsyncRetryPolicy
// and someCircuitBreaker - AsyncCircuitBreakerPolicy
}
}
then I have another repo class
public class SomeRepo : ISomeRepo
{
private readonly AsyncPolicy _somePolicy;
public SomeRepo(..., SomeClass someClass) : base(...)
{
_somePolicy = someClass.PropName;
}
public async Task<Result<SomeDTO>> GetDTO(Guid someId)
{
var someResponse = await _somePolicy.ExecuteAsync(() =>
HttpClient.GetAsync(serviceName, $"endpointUrl"));
...
}
}
2 pieces of code above can't be changed cause they are in prod and I as a junior dev just have to cover code with tests if possible
I have tried to write a test like this
[TestMethod]
public async Task DoStuff()
{
var repository = DefaultSome();
var result = await repository.GetDTO(new Guid());
result.ShouldNotBeNull(); // don't pay attention I'll change stuff which has to be asserted
}
private SomeRepo DefaultSome(Some some = null)
{
some = some ?? A.Fake<ISome>();
/// HERE I TRIED TO MOCK STUFF IN DIFFERENT WAYS AND I HAVE AN ERROR
var policyWrap = A.Dummy<AsyncPolicyWrap>();
//var test = Policy.WrapAsync(A.Fake<AsyncRetryPolicy>(), A.Fake<AsyncCircuitBreakerPolicy>());
//var test = Policy.WrapAsync(A.Fake<IAsyncPolicy>(), A.Fake<IAsyncPolicy>());
A.CallTo(() =>
policyWrap.ExecuteAsync(A<Func<Task<HttpResponseMessage>>>._))
.Returns(new HttpResponseMessage(HttpStatusCode.OK));
var policy = A.Fake<RetryPolicies>();
A.CallTo(() =>
policy.PropName)
.Returns(policyWrap);
return new SomeRepo(some, ..., policy);
}
here is an error i get
I get similar for commented // var test = ... variats
Concrete vs Abstract
Whenever you need to mock something then rely on abstraction rather than concrete implementation.
AsyncPolicyWrap is a concrete class not an abstract like AsyncPolicy
Also as the exception says this class does not have a public parameterless constructor.
It has an internal ctor with 2 parameters:
internal AsyncPolicyWrap(AsyncPolicy outer, IAsyncPolicy inner)
: base(outer.ExceptionPredicates)
{
_outer = outer;
_inner = inner;
}
So, you should prefer AsyncPolicy abstract class or IAsyncPolicy interface.
With or without result
Please be aware that in Polly each Policy has two versions:
One which does not return any result
One which does return some result
Based on the SomeRepo's code your Policy should return an HttpResponseMessage.
So, you should use IAsyncPolicy<HttpResponseMessage> or AsyncPolicy<HttpResponseMessage> to indicate that your policy will return an HttpResponseMessage.
Mocking
Whenever you mock an IAsyncPolicy<HttpResponseMessage> then you don't have to recreate the combined policy (like you did in the comments). All you have to do is to define how should the ExecuteAsync behave.
Happy path:
var mockedPolicy = new Mock<IAsyncPolicy<HttpResponseMessage>>();
mockedPolicy
.Setup(policy => policy.ExecuteAsync(It.IsAny<Func<Task<HttpResponseMessage>>>()))
.ReturnsAsync(new HttpResponseMessage(HttpStatusCode.OK));
Unhappy path:
var mockedPolicy = new Mock<IAsyncPolicy<HttpResponseMessage>>();
mockedPolicy
.Setup(policy => policy.ExecuteAsync(It.IsAny<Func<Task<HttpResponseMessage>>>()))
.ThrowsAsync(new HttpRequestException("Something bad happened"));
I've used moq to mock the policy but the same concept can be applied for FakeItEasy.
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 have the following (simplified) code.
public class Controller
{
private readonly IService _service;
public Controller(IService service)
{
_service = service;
}
public async Task<IHttpActionResult> Create(MyObject object)
{
var result = _service.method(object);
if (!result.Succeeded)
{
return this.GetErrorResult(object);
}
}
}
and SimpleInjector is used to inject the dependency between _service and its implementation class, like this:
public static void Register(Container container)
{
container.Register<IService, Service>();
}
As a note, injection and unit testing are new to me so I do not fully understand them, but am learning.
If I run the application through Swagger, all is working fine.
As a note, the Register function is called when I run the application through Swagger.
Now, I am trying to setup some unit tests using NUnit, and am Mocking the IService object like this:
var Service = new Mock<IService>();
Controller _controller = new Controller(Service.Object);
_controller.Create(new MyObject object());
which seems to be correct to me so far - although I am not sure?
The problem is that for the unit test, result is always null - I think the is because there is a problem with my Mock of the interface - it does not seem to be finding the method - it never steps into it and does not show up int he debugger.
As a note, for the unit test, the Register method does not get called. I did try calling it to register the dependency, but it does not help.
As I said above, this is all new to me and I am on the edge of my understanding on all of this.
I am out of ideas and do not know where to look from here, so any help would be greatly appreciated.
EDIT:
The original question had the following:
public async Task<IHttpActionResult> Create(string content)
which I have updated to:
public async Task<IHttpActionResult> Create(MyObject object)
Can anyone advise how I can pass in a generic reference to MyObject on the setup, without having to make an instance of this class.
So basically I want to tell it that an instance of this class will be passed in, without creating that instance.
I have tried the following:
Service.Setup(x => x.method(It.IsAny<MyObject>())
but it says cannot convert MethodGroup to MyObject
and here is the definition of IService:
public interface IService
{
IdentityResult method(ApplicationUser user, UserLoginInfo login);
}
You need to configure the Mock object to return something for IService.method as follows:
var Service = new Mock<IService>();
Service.Setup(x => x.method(It.IsAny<string>())
.Returns<string>(str => **whatever result you need**);
With the addition of your actual IService definition, you should change the Setup call to:
Service.Setup(x => x.method(It.IsAny<ApplicationUser>(), It.IsAny<UserLoginInfo>())
.Returns<ApplicationUser, UserLoginInfo>((user, login) => new IdentityResult(true));
The setup method has to be called on the Mock object.
var Service = new Mock<IService>();
Service.Setup(x=>x.method("argument")).Returns(YourReturnObject)
Controller _controller = new Controller(Service.Object);
Using your simplified example
public class Controller
{
private readonly IService _service;
public Controller(IService service)
{
_service = service;
}
public async Task<IHttpActionResult> Create(string content)
{
var result = await _service.method(content);
if (!result.Succeeded)
{
return this.GetErrorResult(result);
}
return Ok();
}
}
Lets assume IService is defined as
public interface IService {
Task<Result> method(string input);
}
public class Result {
public bool Succeeded { get; set; }
}
For the unit test you need to setup the mock to fake the actions wanted for the test
public async Task Controller_Given_Content_Should_Return_Ok() {
//Arrange
var input = "content";
var mockService = new Mock<IService>();
mockService
.Setup(m => m.method(input))
.ReturnAsync(new Result { Succeeded = true });
var _controller = new Controller(mockService.Object);
//Act
var result = await _controller.Create(input);
//Assert
Assert.IsNotNull(result);
Assert.IsInstanceOfType(result,typeof(OkResult));
}
Given that the method under test is asynchronous you would want to setup the test to be asynchronous as well.
Please help me with my issue. I have a Service class which is given below:
public class RateService:IRatesService
{
...
public RatesDTO GetById(int Id)
{
return Mapper.Map<Rates, RatesDTO>(this.db.Rates.GetAll().Where(m => m.RateId == Id).First());
}
}
An interface IRatesServicelooks like that sample of code:
public interface IRatesService
{
.....
RatesDTO GetById(int Id);
....
}
And now I try to test public RatesDTO GetById(int Id) method. My code is given below:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
Mock<IRatesService> mock = new Mock<IRatesService>();
mock.Setup(m => m.GetById(It.IsAny<int>())).Returns<RatesDTO>(total=>total);
Assert.IsNotNull(mock.Object.GetById(1));
}
}
But when I run test I get an error like this:
Test Name: TestMethod1 Test
FullName: Provider.Tests.Services.UnitTest1.TestMethod1
Result Message:
Test method Provider.Tests.Services.UnitTest1.TestMethod1 threw
exception: System.ArgumentException: Невозможно преобразовать объект
типа "System.Int32" к типу "Provider.BLL.DTO.RatesDTO".
What is the best practice to test the Service classes and methods?
You are trying to test your mock. That doesn't make any sense.
You should strive to test your actual code.
In your case, you might want to make sure that your GetById returns RatesDTO with the right id value.
You could use Mock framework, to facilitate your testing.
e.g. if you are trying to perform a unit-test and you are using a DB layer you might want to mock the database layer GetAll function and return several objects and then run a test to check that you actually return the right object (same id).
Your RateService is the system under test. When creating mocks for your unit tests the norm is to mock the dependencies of your system under test.
So given your current service lets say it has a dependency on a data store.
public class RateService : IRatesService {
private readonly IDbContext db;
public RateService(IDbContext dbContext) {
this.db = dbContext;
}
//...
public RatesDTO GetById(int Id) {
return Mapper.Map<Rates, RatesDTO>(this.db.Rates.GetAll().Where(m => m.RateId == Id).First());
}
//...
}
IDbContext would be the dependency of the system under test.
You would mock that up when testing RateService
[TestClass]
public class RateServiceUnitTests {
[TestMethod]
public void Given_ValidId_GetById_Should_Return_Dto() {
//Arrange
var validId = 1;
var fakes = new List<Rates>() {
new Rates { RateId = validId }
};
var mock = new Mock<IDbContext>();
//Assuming IDbContext.Rates.GetAll() returns an IEnumerable<Rates>
mock.Setup(m => m.Rates.GetAll()).Returns(fakes);
var sut = new RateService(mock.Object);
//Act
var result = sut.GetById(validId);
//Assert
Assert.IsNotNull(result);
}
}
Noticed that you are also using a mapper. You should make sure that is configured for the test as well otherwise the test will fail. When using static calls in your classes they can cause issues when you try to isolate your system for testing. Try adding the mapper as a dependency as well.