i'm trying to make a test in my example projet, and i'm getting an error when i assert if the deleted object is null(Assert.IsNull failed).
I'm using Microsoft Visual Studio 2010, Entity Framework 4.3.1 and regular Moq.
Here is the code i'm using:
Service:
private ClientRepository rep;
public ClientService(MyContext ctx)
{
this.rep = new ClientRepository(ctx);
}
public void Delete(Client client)
{
try
{
rep.Delete(client);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
throw new Exception("error");
}
}
public Client GetById(int Id)
{
return rep.GetById(Id);
}
Repository:
private MyContext _dbContext;
public ClientRepository(MyContext ctx)
{
_dbContext = ctx;
}
public void Delete(Client client)
{
Client cl = _dbContext.Clients.Where(x => x.Id == client.Id).FirstOrDefault();
if (cl != null)
{
_dbContext.Clients.Remove(client);
_dbContext.SaveChanges();
}
else
{
throw new Exception("Client does'n exist");
}
}
public Client GetById(int Id)
{
return _dbContext.Clients.Where(x => x.Id == Id).FirstOrDefault();
}
My Delete Test:
[TestMethod]
public void MockDelete()
{
//arrange
var data = new List<Client>
{
new Client(){
Id = 1,
Name = "asdfg"
}
}.AsQueryable();
var dbSetMock = new Mock<IDbSet<Client>>();
var myContextMock = new Mock<MyContext>();
dbSetMock.As<IQueryable<Client>>().Setup(m => m.Provider).Returns(data.Provider);
dbSetMock.As<IQueryable<Client>>().Setup(m => m.Expression).Returns(data.Expression);
dbSetMock.As<IQueryable<Client>>().Setup(m => m.ElementType).Returns(data.ElementType);
dbSetMock.As<IQueryable<Client>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
myContextMock.Setup(m => m.Clients).Returns(dbSetMock.Object);
var service = new ClientService(myContextMock.Object);
//act
var classtodelete = data.First(m => m.Id == 1);
service.Delete(classtodelete);
string name = service.GetById(1).Name;
//assert
dbSetMock.Verify(m => m.Remove(It.IsAny<Client>()), Times.Once());
myContextMock.Verify(m => m.SaveChanges(), Times.Once);
Assert.IsNull(name);
}
My goal is to test the business rules of my Service(i didn't put here), maybe i mocked the wrong classes, or i'm missing something, i hope someone can help me.
Related
I have a service name "DataAccessService". In that service we are calling Procedure and other methods from AppDbContext.
I am writing Unit Test cases for DataAccessService methods. We have method called IsDatabaseUp() which will give response if connection with databases success or not.
When I am trying to access AppDbContext is null which is breaking the unit test method.
DataAccessService:
public bool IsDatabaseUp()
{
try
{
int counter = 1;
while (counter <= EnvConstants.DatabaseCounter)
{
try
{
using (var scope = _serviceScopeFactory.CreateScope())
{
AppDbContext dbContext = scope.ServiceProvider.GetService<AppDbContext>();
//Here dbContext is null
bool isDbUp = dbContext.IsDatabaseUp;
if (isDbUp)
return isDbUp;
counter++;
}
}
catch (Exception ex)
{
counter++;
}
}
}
catch (Exception ex)
{
}
return false;
}
DataServiceTests:
private readonly IDataAccessService _dataAccessService;
private readonly Mock<IServiceScopeFactory> _serviceScopeFactory;
public DataAccessServiceTest()
{
//Arrange
var serviceProvider = new Mock<IServiceProvider>();
serviceProvider
.Setup(x => x.GetService(typeof(AppDbContext)));
var serviceScope = new Mock<IServiceScope>();
serviceScope.Setup(x => x.ServiceProvider).Returns(serviceProvider.Object);
_serviceScopeFactory = new Mock<IServiceScopeFactory>();
_serviceScopeFactory
.Setup(x => x.CreateScope())
.Returns(serviceScope.Object);
serviceProvider
.Setup(x => x.GetService(typeof(IServiceScopeFactory)))
.Returns(_serviceScopeFactory.Object);
var serviceCollection = new ServiceCollection();
serviceCollection.AddMemoryCache();
var serviceProvider1 = serviceCollection.BuildServiceProvider();
var memoryCache = serviceProvider1.GetService<IMemoryCache>();
var inMemoryCahce= new InMemoryCahce(memoryCache);
_dataAccessService = new DataAccessService(_serviceScopeFactory.Object);
_cacheService = new ServiceOfCahe(inMemoryCahce, _dataAccessService);
LoadMemoryData();
}
[Fact]
public void GetMasterDataInfromationFromSql_Test()
{
var testResult = _dataAccessService.IsDatabaseUp();
}
Can you please help me on this.
Line var job = _mapper.Map<DataAccess.Domain.Lab.Job>(jobViewModel); gives error
job variable is always returning null while running the unit test
i have added the mapping for Jobs Profile `
Below is the code:
JobTest.cs class
public class JobTests
{
private static Mock<IMapper> _mapper;
public JobTests()
{
if (_mapper == null)
{
_mapper = new Mock<IMapper>();
_mapper.Setup(x => x.ConfigurationProvider.AssertConfigurationIsValid());
_mapper.Setup(x => x.ConfigurationProvider)
.Returns(
() => new MapperConfiguration(
cfg =>
{
cfg.AddProfile<JobsProfile>();
//cfg.CreateMap<AddJobCommand,JobsProfile > ();
//cfg.CreateMap<JobViewModel, AddJobCommand>();
//cfg.CreateMap<AddJobCommand,JobViewModel>();
}));
}
}
[Fact]
public async Task AddJob_AddSingleEntry()
{
var mapperMock = new Mock<IMapper>();
var data = JobData.AddFakeJobList();
var mockSet = FakeDbSetup.GetMockDbSet<DataAccess.Domain.Lab.Job>(data);
var mockContext = FakeDbSetup.GetMockDbContext();
mockSet.Setup(x => x.AsNoTracking()).Returns(mockSet.Object);
mockContext.Setup(c => c.Jobs).Returns(mockSet.Object);
AddJobCommandHandler handler = new AddJobCommandHandler(mockContext.Object, _mapper.Object);
JobViewModel vm= JobData.AddFakeJobList2();
AddJobCommand command = new AddJobCommand(vm);
//var stubScheduleCommand = new Mock<AddJobCommand>(mockContext.Object);
var job = await handler.Handle(command, new System.Threading.CancellationToken());
Assert.NotNull(job);
}
}
AddJobCommand.cs
public class AddJobCommandHandler : IRequestHandler<AddJobCommand, JobViewModel>
{
private readonly IDrillingFluidsContext _context;
private readonly IMapper _mapper;
public AddJobCommandHandler(IDrillingFluidsContext context, IMapper mapper)
{
(_context, _mapper) = (context, mapper);
}
public async Task<JobViewModel> Handle(AddJobCommand command, CancellationToken cancellationToken)
{
if (command.JobViewModel == null) throw new InvalidOperationException("Empty request.");
var jobViewModel = command.JobViewModel;
try
{
var job = _mapper.Map<DataAccess.Domain.Lab.Job>(jobViewModel);
_context.Set<DataAccess.Domain.Lab.Job>().Add(job);
if (job.Notes!= null)
{
var newNote = job.Notes.FirstOrDefault(n => n.IsNew);
if (newNote != null)
{
newNote.JobId = job.Id;
_context.Set<DataAccess.Domain.Lab.JobNote>().Attach(newNote);
_context.Entry(newNote).State = EntityState.Added;
}
}
if (string.IsNullOrWhiteSpace(job.Name))
{
job.Name = await GenerateJobName(job);
}
await _context.SaveChangesAsync();
jobViewModel.Id = job.Id;
return jobViewModel;
}
catch (DbEntityValidationException e)
{
foreach (var eve in e.EntityValidationErrors)
{
Console.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
eve.Entry.Entity.GetType().Name, eve.Entry.State);
foreach (var ve in eve.ValidationErrors)
{
Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"",
ve.PropertyName, ve.ErrorMessage);
}
}
throw;
}
catch (DbUpdateException e)
{
throw;
}
catch (Exception e)
{
throw;
}
}
I want the JobViewModel data to be added to the the variable jobs.But it always returns null.This works fine when i am trying to call this method via PostMan.
The problem is that the code uses a mocked mapper that has no setup for the Map method. If there is no setup, the method will return null (for
MockBehavior.Loose`).
If you want the Map method to return a value, you need add a setup, e.g.:
[Fact]
public async Task AddJob_AddSingleEntry()
{
var data = JobData.AddFakeJobList();
var mockSet = FakeDbSetup.GetMockDbSet<DataAccess.Domain.Lab.Job>(data);
var mockContext = FakeDbSetup.GetMockDbContext();
mockSet.Setup(x => x.AsNoTracking()).Returns(mockSet.Object);
mockContext.Setup(c => c.Jobs).Returns(mockSet.Object);
AddJobCommandHandler handler = new AddJobCommandHandler(mockContext.Object, _mapper.Object);
JobViewModel vm= JobData.AddFakeJobList2();
AddJobCommand command = new AddJobCommand(vm);
//var stubScheduleCommand = new Mock<AddJobCommand>(mockContext.Object);
var job = new DataAccess.Domain.Lab.Job()
{
// Initialize job as needed
};
_mapper.Setup(x => x.Map<DataAccess.Domain.Lab.Job>(vm))
.Returns(job);
var job = await handler.Handle(command, new System.Threading.CancellationToken());
Assert.NotNull(job);
}
In the constructor it is not necessary to add setups for the configuration provider if you are using a mocked mapper. So you can setup the _mapper simply like this:
public JobTests()
{
_mapper = new Mock<IMapper>();
}
I'm trying to write an Unit Test for my ASP.Net Core application with XUnit framework and MOQ and am trying to test the below method(snippet given below):
public async Task<IActionResult> Save([FromBody] DTO.ContactUs contactUs)
{
contactUs.FirstName = _htmlEncoder.Encode(contactUs.FirstName);
contactUs.LastName = _htmlEncoder.Encode(contactUs.LastName);
contactUs.EmailAddress = _htmlEncoder.Encode(contactUs.EmailAddress);
contactUs.Phone = _htmlEncoder.Encode(contactUs.Phone);
if (HttpContext.User.CurrentClient() != null)
contactUs.ClientId = HttpContext.User.CurrentClient().ClientId;
contactUs.UserId = User.GetUserId();
string dbName = HttpContext.User.CurrentClient().ConnectionString;
var result = _clientService.AddNewContactUs(contactUs, dbName);
if (result)
{
try
{
int clientId = HttpContext.User.CurrentClient().ClientId;
var clientDetails = _clientService.GetClientDetailsByClientID(clientId);
// Lines of code...
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
return Json(result);
}
While I can mock all the other dependent services, I'm kind of stuck with the HttpContext part. I am not able to mock the HttpContext.User.CurrentClient() part where HttpContext.User is of type ClaimsPrincipal and CurrentClient is an user-defined function, defined as:
public static Client CurrentClient(this ClaimsPrincipal principal)
{
if (!string.IsNullOrEmpty(principal.Claims.Single(p => p.Type.Equals(AppClaimTypes.CurrentClient)).Value))
{
int clientId = Convert.ToInt32(principal.Claims.Single(p => p.Type.Equals(AppClaimTypes.CurrentClient)).Value);
return principal.GetClients().Where(c => c.ClientId == clientId).FirstOrDefault();
}
else
{
return null;
}
}
This is my UnitTest class that I have managed to write till now:
public class ContactUsControllerTests
{
private Mock<IClientService> clientServiceMock;
private Mock<IWebHostEnvironment> webHostEnvironmentMock;
private Mock<HtmlEncoder> htmlEncoderObjMock;
private Mock<IEmailNotification> emailNotificationMock;
private Mock<HttpContext> mockContext;
private Mock<HttpRequest> mockRequest;
private Mock<ClaimsPrincipal> mockClaimsPrincipal;
private ContactUs contactUsObj = new ContactUs()
{
FirstName = "TestFN",
LastName = "TestLN",
EmailAddress = "testemail#gmail.com",
Phone = "4564560000",
Comments = "This is just a test"
};
private ClaimsPrincipal principal = new ClaimsPrincipal();
public ContactUsControllerTests()
{
clientServiceMock = new Mock<IClientService>();
webHostEnvironmentMock = new Mock<IWebHostEnvironment>();
htmlEncoderObjMock = new Mock<HtmlEncoder>();
emailNotificationMock = new Mock<IEmailNotification>();
mockRequest = new Mock<HttpRequest>();
mockContext = new Mock<HttpContext>();
// set-up htmlEncoderMock
htmlEncoderObjMock.Setup(h => h.Encode(contactUsObj.FirstName)).Returns(contactUsObj.FirstName);
htmlEncoderObjMock.Setup(h => h.Encode(contactUsObj.LastName)).Returns(contactUsObj.LastName);
htmlEncoderObjMock.Setup(h => h.Encode(contactUsObj.EmailAddress)).Returns(contactUsObj.EmailAddress);
htmlEncoderObjMock.Setup(h => h.Encode(contactUsObj.Phone)).Returns(contactUsObj.Phone);
htmlEncoderObjMock.Setup(h => h.Encode(contactUsObj.Comments)).Returns(contactUsObj.Comments);
// set-up mockContext
mockContext.Setup(m => m.Request).Returns(mockRequest.Object);
mockContext.Object.User.CurrentClient().ClientId = 30; // this throws error
//other initialisations
}
[Fact]
public async void SaveMethodTest()
{
ContactUsController contactUsControllerObj = new ContactUsController(clientServiceMock.Object, webHostEnvironmentMock.Object, htmlEncoderObjMock.Object, emailNotificationMock.Object);
// Act
await contactUsControllerObj.Save(contactUsObj);
// Arrange
// Lines of code
}
}
Any help whatsoever on this would very helpful.
public Fixture()
{
_server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>()
.ConfigureServices(services =>
{
services.AddScoped<ICarService, CarService>();
}));
Client = _server.CreateClient();
}
from tests I'm using this HttpClient to test my API.
using (var response = await _client.GetAsync($"/api/car/{id}"))
{
//...
}
The thing is that I want to fake the result of the GetAsync(int id) method in CarService class.
So I tried
var myCarObject = ... omitted for clarity
var myCarMockService = new Mock<ICarService>();
myCarMockService.Setup(x => x.GetAsync(It.IsAny<int>())).Returns(Task.FromResult(myCarObject));
I don't know is this right approach, but if it is how can I inject it
into Fixture class so CarService can use it.
public class CarService: ICarService {
private readonly CarDbContext _carDbContext;
public CarService(CarDbContext carDbContext)
{
_carDbContext = carDbContext;
}
public async Task<Car> GetAsync(int id)
{
return await _carDbContext.Cars.FindAsync(id);
}
}
Update:
private readonly ICarService _carService;
public CarController(ICarService carService)
{
_carService = carService;
}
public async Task<IActionResult> Get([FromRoute] int id)
{
var car = await _carService.GetAsync(id);
}
Update 2:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<CarDbContext>(options => { options.UseSqlServer(Configuration.GetConnectionString("Db")); });
services.AddTransient<ICarService, CarService>();
}
}
public class CarService : ICarService
{
private readonly CarDbContext _carDbContext;
public ContactService(CarDbContext carDbContext)
{
_carDbContext= carDbContext;
}
public async Task<Owner> GetAsync(int ownerId)
{
var owner = await _carDbContext.Owners.FindAsync(ownerId);
return owner.Car;
}
}
Update 3:
private readonly TestServer _server;
public Fixture()
{
var dbContextOptions = new DbContextOptionsBuilder<CarDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
var mockContext = new Mock<CarDbContext>(dbContextOptions);
var mockOwnerSet = new Mock<DbSet<Owner>>();
var mockCarSet = new Mock<DbSet<Car>>();
mockContext.Setup(m => m.Owners).Returns(mockOwnerSet.Object);
mockContext.Setup(m => m.Cars).Returns(mockCarSet.Object);
var carService = new CarService(mockContext.Object);
_server = new TestServer(new WebHostBuilder()
.ConfigureAppConfiguration((context, conf) =>
{
conf.AddJsonFile(#Directory.GetCurrentDirectory() + "../appsettings.json");
}).UseStartup<Startup>()
.ConfigureServices(services =>
{
services.AddDbContext<CarDbContext>(options => options.UseInMemoryDatabase("Test"));
services.AddScoped<ICarService>(_ => carService);
})
);
Client = _server.CreateClient();
}
Configure the test server to use the mocked service
public Fixture() {
Car myCarObject = //... omitted for brevity
var myCarMockService = new Mock<ICarService>();
myCarMockService
.Setup(x => x.GetAsync(It.IsAny<int>()))
.ReturnsAsync(myCarObject);
_server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>()
.ConfigureTestServices(services => {
var serviceDescriptor = services.FirstOrDefault(descriptor => descriptor.ServiceType == typeof(ICarService));
if (serviceDescriptor != null) services.Remove(serviceDescriptor);
services.AddTransient<ICarService>(_ => myCarMockService.Object); // <-- NOTE
})
);
Client = _server.CreateClient();
}
That way when the call is made the mocked service will be injected as expected.
Mock<IDbContext> dbContext;
[TestFixtureSetUp]
public void SetupDbContext()
{
dbContext = new Mock<IDbContext>();
dbContext.Setup(c => c.SaveChanges()).Verifiable();
dbContext.Setup(c => c.SaveChangesAsync()).Verifiable();
dbContext.Setup(c => c.Customers.Add(It.IsAny<Customer>()))
.Returns(It.IsAny<Customer>()).Verifiable();
}
[Test]
public async Task AddCustomerAsync()
{
//Arrange
var repository = new EntityFrameworkRepository(dbContext.Object);
var customer = new Customer() { FirstName = "Larry", LastName = "Hughes" };
//Act
await repository.AddCustomerAsync(customer);
//Assert
dbContext.Verify(c => c.Customers.Add(It.IsAny<Customer>()));
dbContext.Verify(c => c.SaveChangesAsync());
}
[Test]
public void AddCustomer()
{
//Arrange
var repository = new EntityFrameworkRepository(dbContext.Object);
var customer = new Customer() { FirstName = "Larry", LastName = "Hughes" };
//Act
repository.AddCustomer(customer);
//Assert
dbContext.Verify(c => c.Customers.Add(It.IsAny<Customer>()));
dbContext.Verify(c => c.SaveChanges());
}
And here's what I want to test:
public class EntityFrameworkRepository
{
private readonly IDbContext DBContext;
public EntityFrameworkRepository(IDbContext context)
{
DBContext = context;
}
public async Task AddCustomerAsync(Customer customer)
{
DBContext.Customers.Add(customer);
await DBContext.SaveChangesAsync();
}
public void AddCustomer(Customer customer)
{
DBContext.Customers.Add(customer);
DBContext.SaveChanges();
}
}
AddCustomers test passes.
AddCustomersAsync test fails, I keep getting a NullReferenceException after calling await DbContext.SaveChangesAsync().
at
MasonOgCRM.DataAccess.EF.EntityFrameworkRepository.d__2.MoveNext()
in
C:\Users\Mason\Desktop\Repositories\masonogcrm\src\DataAccess.EFRepository\EntityFrameworkRepository.cs:line
43
I can't see anything that's null in my code. DbContext is not null. The equivalent test of AddCustomers which is identical with the exception of not being async runs as expected. I suspect I haven't performed a correct setup of SaveChangesAsync in SetupDBContext() but I don't know what to do to fix it.
You are right the problem occurs because one of your setups incorrect :: dbContext.Setup(c => c.SaveChangesAsync()).Verifiable();.
The method return a Task and you forgot to return a Task therefore null returns.
You can remove dbContext.Setup(c => c.SaveChangesAsync()).Verifiable(); or
change the setup to something like:
dbContext.Setup(c => c.SaveChangesAsync()).Returns(() => Task.Run(() =>{})).Verifiable();
You could use
dataContext.Setup(x => x.SaveChangesAsync()).ReturnsAsync(1);
and
dataContext.Verify(x=>x.SaveChangesAsync());
This worked for me:
dbContext.Verify(m => m.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once());
It may also require a cancellation token, so something like this should work:
dataContext.Setup(x => x.SaveChangesAsync(It.IsAny<CancellationToken>())).ReturnsAsync(1);