I tried to write unit test for getMark() and faced problem with Moq, with which I'm not familiar. I have no idea what method and object properly mock in order to unit test getMark()
Here is my MarkServiceClass containing getMark()
public class MarkService : IMarkService
{
IMarkService _markService;
IStdService _stdService;
IStdService _stdMService;
RClass cs;
public MarkService(IMarkService markService, IStdService stdService, IStdService stdMService)
{
_markService = markService;
_stdService = stdService;
_stdMService = stdMService;
}
public bool Init(int sID, string pID, string year)
{
try
{
cs = new RClass ();
cs.sLevel = __stdService.GetAsIQueryable().FirstOrDefault(x => x.UID == pID);
var mInfo = __stdMService.GetSTDM(sID, pID, year);
cs.Type = mInfo.CalculateAmount;
return true;
}
catch
{
return false;
}
}
public MarkVM getMark(int sID, string pID, string year)
{
var output=Init(sID, pID, year);
if (!output)
return null;
int sGrade= 0;
int sMark= 0;
//here are conditions where sGrade and sMark used
return new MarkVM
{
Grade = sGrade,
Mark = sMark
};
}
}
and MarkVM
public class MarkVM
{
public int Grade { get; set; }
public int Mark { get; set; }
}
The code you shared is not complete so I had to make some assumptions to give you an example how to unit test getMark
public class MarkVM
{
public int Grade { get; set; }
public int Mark { get; set; }
}
Not knowing what RClass is, I define it with minimal requirements
public class RClass {
public String Uid { get; set; }
public string sLevel { get; set; }
public int Type { get; set; }
}
Same for this Info your service retrieves with GetSTDM
public class Info
{
public int CalculateAmount { get; set; }
}
Now come the interfaces. This is definitely required if you want to mock
public interface IStdService
{
List<RClass> GetAsIQueryable();
Info GetSTDM(int sID, string pID, string year);
}
Those 2 methods are the ones you'll want to mock if you unit test getMark.
Mocking getMark itself will only allow you to check it is called, but not its behavior which is the purpose of unit testing.
Now the main class. I removed the injection of IMarkService in the constructor because I really don't see why you would do that: Markservice implements IMarkService here.
For any reason you use 2 instances of IStdService, I kelpt that but then you need to inject it too.
public class MarkService : IMarkService
{
private IStdService __stdService;
private IStdService __stdMService;
public RClass cs;
public MarkService(IStdService stdMService, IStdService stdService)
{
__stdMService = stdMService;
__stdService = stdService;
}
public bool Init(int sID, string pID, string year)
{
try
{
cs = new RClass();
cs.sLevel = __stdService.GetAsIQueryable().FirstOrDefault(x => x.Uid == pID).sLevel;
var mInfo = __stdMService.GetSTDM(sID, pID, year);
cs.Type = mInfo.CalculateAmount;
return true;
}
catch
{
return false;
}
}
public MarkVM getMark(int sID, string pID, string year)
{
var output = Init(sID, pID, year);
if (!output)
return null;
int sGrade = 0;
int sMark = 0;
//here are conditions where sGrade and sMark used
return new MarkVM
{
Grade = sGrade,
Mark = sMark
};
}
}
Now comes the test. If you want to unit test getMark you could either mock Init from IMarkService, or consider the behavior comes from this Init and then you want to mock GetAsIQueryable and GetSTDM.
I made the assumption second option is what you want.
using System.Collections.Generic;
using MarkServiceNS;
using Moq;// Moq framework where you'll find everything you need
using NUnit.Framework;// Using NUnit for unit test. Because I like it :-)
using NUnit.Framework.Constraints;
namespace UnitTestWithMoqExample
{
public class Tests
{
[SetUp]
public void Setup()
{
}
[Test]
public void getMark()
{
var mockedStdService = new Mock<IStdService>();
mockedStdService.Setup(x => x.GetAsIQueryable())
.Returns(new List<RClass> { new RClass { Uid = "uid", sLevel = "expected", Type = 1 } }); // Here you define what it the mocked result of GetAsIQueryable call.
var mockedStdMService = new Mock<IStdService>();
mockedStdMService.Setup(x => x.GetSTDM(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<string>()))
.Returns(new Info { CalculateAmount = 1 });// Same here. You mock GetSTDM. The method parameters are not expected to change the behavior in my unit test, this is why I consider It.Any<T> so whatever you pass to the mock, the result will be the same.
// Here is the assertion. This should do the job
var service = new MarkServiceNS.MarkService(mockedStdMService.Object, mockedStdService.Object);
Assert.IsNotNull(service.getMark(1, "", ""));
Assert.IsInstanceOf(typeof(MarkVM), service.getMark(1, "", ""));
Assert.AreEqual(0, service.getMark(1, "", "").Grade);
Assert.AreEqual(0, service.getMark(1, "", "").Mark);
}
}
}
A basic Moq coding will be like this
[Test]
public void Test1()
{
var mock = new Mock<IMarkService>();
mock.Setup(p => p.getMark(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<string>())).Returns(new MarkVM());
mock.Verify(p => p.getMark(1001, "P001", "2022"), Times.Once());
}
I am posting this as an example as I don't have your full code
Use the above technique to moq your methods GetAsIQueryable and GetSTDM and CalculateAmount
And call the method Init and then call the getMark
Related
I'm currently studying C# and I'm quiet stunned over a simple task.
I have this code to test:
public interface IAppointment
{
public string PatientName { get; set; }
public IEnumerable<DateTime> ProposedTimes { get; set; }
public DateTime? SelectedAppointmentTime { set; }
}
public static class MedicalScheduler
{
public static Dictionary<DateTime, string> Appointments { get; set; } = new Dictionary<DateTime, string>();
public static List<DateTime> FreeSlots { get; set; } = new List<DateTime>();
public static IEnumerable<Tuple<string, bool>> Schedule(IEnumerable<IAppointment> requests)
{
bool slotFound = false;
foreach (var appointment in requests)
{
if (slotFound) continue;
foreach (var times in appointment.ProposedTimes)
{
var freeSlot = FreeSlots.Where(s => s.Date == times.Date).FirstOrDefault();
if (freeSlot != null)
{
slotFound = true;
Appointments.Remove(freeSlot);
appointment.SelectedAppointmentTime = freeSlot;
yield return new Tuple<string, bool>(appointment.PatientName, true);
}
}
yield return new Tuple<string, bool>(appointment.PatientName, false);
}
}
}
And I'm required to test "Schedule" with a certain set of parameters. For example, I need to test it with empty Appointments and FreeList but with a single element in "requests".
I think I have understood how to compile a Unit Test and to set the Dictionary and List parameters. But I'm not sure how to create the IEnumerable variable.
My idea was to create a List of IAppointment(s), but how can I implement the interface in the test unit? I have tried using Moq but I didn't understood how I should use it correctly.
I'm sorry if the request seems quite confusing, but I don't know how to explain better :)
Thanks in advance for the help.
Please see the following example:
[Test]
public void Schedule()
{
// Arrange
var appointmentMock = new Mock<IAppointment>();
appointmentMock.Setup(appointment => appointment.PatientName).Returns("Dixie Dörner");
appointmentMock.Setup(appointment => appointment.ProposedTimes).Returns(
new List<DateTime>
{
new DateTime(1953,4,12),
new DateTime(1953,4,13)
});
var requests = new List<IAppointment>{appointmentMock.Object};
// Act
var results = MedicalScheduler.Schedule(requests);
// Assert
Assert.IsTrue(results.Any());
// results.Should().HaveCount(1); // If you're using FluentAssertions
}
MedicalScheduler.Schedule accepts any parameter implementing IEnumerable<IAppointment>, e. g. List<IAppointment> or Collection<IAppointment>.
So you simply create a List<IAppointment> and fill it with custom instances of IAppointment.
You can use Moq for creating the instances, as I did in the example. But for my own projects, I prefer the builder pattern:
internal static class AppointmentBuilder
{
public static IAppointment CreateDefault() => new Appointment();
public static IAppointment WithPatientName(this IAppointment appointment, string patientName)
{
appointment.PatientName = patientName;
return appointment;
}
public static IAppointment WithProposedTimes(this IAppointment appointment, params DateTime[] proposedTimes)
{
appointment.ProposedTimes = proposedTimes;
return appointment;
}
private class Appointment : IAppointment
{
public string PatientName { get; set; }
public IEnumerable<DateTime> ProposedTimes { get; set; }
public DateTime? SelectedAppointmentTime { get; set; }
}
}
[Test]
public void Schedule()
{
// Arrange
var requests = new List<IAppointment>{AppointmentBuilder.CreateDefault()
.WithPatientName("Dixie")
.WithProposedTimes(new DateTime(1953,4,12))};
// Act
var results = MedicalScheduler.Schedule(requests);
// Assert
Assert.IsTrue(results.Any());
// results.Should().HaveCount(1); // If you're using FluentAssertions
}
I'm learning and practicing CQRS and Unit Test in C#. I need to know how to unit test Command Handlers in CQRS.
Here is my CreateAuthorCommand:
public sealed class CreateAuthorCommand : ICommand<Author>
{
public CreateAuthorCommand(string firstName, string lastName, DateTimeOffset dateOfBirth, string mainCategory, IEnumerable<CreateBookDto> books)
{
FirstName = firstName;
LastName = lastName;
DateOfBirth = dateOfBirth;
MainCategory = mainCategory;
Books = books;
}
public string FirstName { get; }
public string LastName { get; }
public DateTimeOffset DateOfBirth { get; private set; }
public string MainCategory { get; }
public IEnumerable<CreateBookDto> Books { get; }
[AuditLog]
[DatabaseRetry]
internal sealed class AddCommandHandler : ICommandHandler<CreateAuthorCommand, Author>
{
private readonly IUnitOfWork _unitOfWork;
public AddCommandHandler(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
}
public async Task<Result<Author>> Handle(CreateAuthorCommand command)
{
var nameResult = Name.Create(command.FirstName, command.LastName);
var birthDateResult = BirthDate.Create(command.DateOfBirth);
var mainCategoryResult = Entities.Authors.MainCategory.Create(command.MainCategory);
var authorResult = Result.Combine(nameResult, birthDateResult, mainCategoryResult)
.Map(() => new Author(nameResult.Value, birthDateResult.Value, null, mainCategoryResult.Value));
if (authorResult.IsFailure)
return Result.Failure<Author>(authorResult.Error);
await _unitOfWork.AuthorRepository.AddAsync(authorResult.Value);
await _unitOfWork.SaveChangesAsync();
return Result.Success(authorResult.Value);
}
}
}
Here is my Unit Test:
public class CreateAuthorCommandTests
{
[Fact]
public void Create_Author_Should_Call_Add_Method_Once()
{
var fixture = new Fixture();
var command = fixture.Create<CreateAuthorCommand>();
var mockUnitOfWork = new Mock<IUnitOfWork>();
var mockHandler = new Mock<ICommandHandler<CreateAuthorCommand, Author>>();
var result = mockHandler.Object.Handle(command);
mockUnitOfWork.Verify(x => x.AuthorRepository.AddAsync(It.IsAny<Author>()), Times.Once);
}
}
When I debug the above test,
I'm not able to step into Handler Method!!
How to pass mockUnitOfWork as constructor parameter to AddCommandHandler?
If I can pass mockUnitOfWork, the I can verify the AddAsync call inside my AuthorRepository. Please assist on what I'm doing wrong.
You are testing the handler, so instead of this
var result = mockHandler.Object.Handle(command);
...create an actual instance of AddCommandHandler and inject the dependencies it requires, i.e.
var mockUnitOfWork = new Mock<IUnitOfWork>();
var handler = new AddCommandHandler(mockUnitOfWork.Object);
When you call in to Handle, you'll now be stepping into your implementation.
If you want to keep the Internal access modifier, you can use InternalsVisibleTo attribute to grant access to your test project, e.g. do this in your project that defines the Handler,
[assembly: InternalsVisibleTo(“UnitTests”)]
https://anthonygiretti.com/2018/06/27/how-to-unit-test-internal-classes-in-net-core-applications/
I am trying to unit test a method within my controller in my Web API using XUnit. The role of the method is to get a single title, by ISBN, from the database. The issue I came across during unit testing is that I am unsure how to insert the dummy data that I must perform the test on, as well as how the Assert function works.
TitleController.cs
[ApiController]
[Route("titlecontroller")]
public class TitleController : Controller
{
private IGtlTitleRepository _gtlTitleRepository;
public TitleController(IGtlTitleRepository gtlTitleRepository)
{
_gtlTitleRepository = gtlTitleRepository;
}
[Route("getTitle/{ISBN}")]
[HttpGet()]
public GtlTitle GetTitle(string ISBN)
{
return _gtlTitleRepository.GetTitle(ISBN);
}
}
IGtlTitleRepository.cs
public interface IGtlTitleRepository
{
GtlTitle GetTitle(string ISBN);
}
MockGtlTitleRepository.cs
public class MockGtlTitleRepository : IGtlTitleRepository
{
private readonly string _connection;
public MockGtlTitleRepository(IOptions<ConnectionStringList> connectionStrings)
{
_connection = connectionStrings.Value.GTLDatabase;
}
private List<GtlTitle> _titleList;
public GtlTitle GetTitle(string ISBN)
{
using (var connection = new SqlConnection(_connection))
{
connection.Open();
return connection.QuerySingle<GtlTitle>("GetTitleByISBN", new { ISBN }, commandType: CommandType.StoredProcedure);
}
}
}
Right, as for my test code, I was able to write the following code, but as I said above, I can't figure out a proper way to test the method.
public class UnitTest1
{
[Fact]
public void Test1()
{
var repositoryMock = new Mock<IGtlTitleRepository>();
var title = new GtlTitle();
repositoryMock.Setup(r => r.GetTitle("978-0-10074-5")).Returns(title);
var controller = new TitleController(repositoryMock.Object);
var result = controller.GetTitle("978-0-10074-5");
// assert??
repositoryMock.VerifyAll();
}
}
What should be done within this unit test in order to properly test the method?
EDIT:
GtlTitle.cs
public class GtlTitle
{
public string ISBN { get; set; }
public string VolumeName { get; set; }
public string TitleDescription { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PublisherName { get; set; }
}
Before going to testing, there are a few things I recommend updating in your code:
Make your repository methods and controller actions async (thus web server can process requests while waiting for database roundtrips for previous calls)
Use ActionResult as an action return type. This way you can send different http status codes to the client.
Return 404 NotFound status code when title not found instead of returning successful result with null as payload.
Consider using a RESTful approach for API endpoints. E.g. base uri for titles resource should be something like api/titles
Don't specify getTitle for getting title endpoint, because you know HTTP verb which endpoint is mapped to (GET) and base resource url (api/titles).
With these notes applied:
[ApiController]
[Route("api/titles")]
public class TitleController : Controller
{
private IGtlTitleRepository _gtlTitleRepository;
public TitleController(IGtlTitleRepository gtlTitleRepository)
{
_gtlTitleRepository = gtlTitleRepository;
}
[HttpGet("{ISBN}")] // GET api/titles/{ISBN}
public async Task<ActionResult<GtlTitle>> GetTitle(string ISBN)
{
var title = await _gtlTitleRepository.GetTitle(ISBN);
if (title == null)
return NotFound();
return title;
}
}
Testing successful title retrieving:
[Fact]
public async Task Should_Return_Title_When_Title_Found()
{
var repositoryMock = new Mock<IGtlTitleRepository>();
var title = new GtlTitle();
repositoryMock.Setup(r => r.Get("978-0-10074-5")).Returns(Task.FromResult(title));
var controller = new TitleController(repositoryMock.Object);
var result = await controller.GetTitle("978-0-10074-5");
Assert.Equal(title, result.Value);
}
When title not found:
[Fact]
public async Task Should_Return_404_When_Title_Not_Found()
{
var repositoryMock = new Mock<IGtlTitleRepository>();
repositoryMock.Setup(r => r.Get("978-0-10074-5")).Returns(Task.FromResult<GtlTitle>(null));
var controller = new TitleController(repositoryMock.Object);
var result = await controller.GetTitle("978-0-10074-5");
Assert.IsType<NotFoundResult>(result.Result);
}
I have person model class defined as:
public class PersonModel
{
public bool SelectionSubmitted = false;
public bool ShowValidationSummary = false;
public string Name;
public string Get()
{
//actual implementation return some value from the db
return string.Empty;
}
}
The controller implementation is as follows:
class HomeController : Controller
{
[HttpGet]
public ActionResult Index(PersonModel model)
{
if (model.SelectionSubmitted && !ValidateSelections(model))
{
model.ShowValidationSummary = true;
}
return View("Index", model.Get());
}
private bool ValidateSelections(PersonModel model)
{
if(model.Name == "")
{
ModelState.AddModelError("EmptyPersonName", "Person name cannot be null");
}
return ModelState.IsValid;
}
}
The test class and method is defined as:
[TestClass]
public class ChildWithoutPlacementControllerTest
{
private readonly Mock<PersonModel> _mockPersonModel;
public ChildWithoutPlacementControllerTest()
{
_mockPersonModel = new Mock<PersonModel>();
}
[TestMethod]
public void GivenPerson_WhenSearchingForFutureBirthDate_ThenValidationMessageShouldBeShown()
{
//Arrange
HomeController controller = new HomeController();
_mockPersonModel.Setup(x => x.Get()).Returns(It.IsAny<string>());
_mockPersonModel.SetupGet(x => x.Name).Returns(string.Empty);
_mockPersonModel.SetupGet(x => x.SelectionSubmitted).Returns(true);
//Act
controller.Index(_mockPersonModel.Object);
//Assert
var isShowSummarySetToTrue = _mockPersonModel.Object.ShowValidationSummary;
Assert.IsTrue(isShowSummarySetToTrue);
}
}
What I want to achieve is mock the SelectionSubmitted and Name property to true and string.Empty respectively also Setup the Get method of PersonModel class, and check if the test return object has ShowValidationSummary set to true.
However, I am getting that I can't set up the non-virtual property Name.
Am I doing something wrong or is there any way to do it without changing the implementation code?
Am I doing something wrong
This appears to be an XY problem.
is there any way to do it without changing the implementation code
There really is no need for moq in this scenario. You can use inheritance to craft a fake model to be used in the test. The fake model will override the method that is tightly coupled to the database. (more on that later)
public class FakePerson : PersonModel {
public new string Get() {
return string.Empty; //Not calling the base Get
}
}
The test can then be refactored to use the fake model and be exercised to completion as intended.
[TestMethod]
public void GivenPerson_WhenSearchingForFutureBirthDate_ThenValidationMessageShouldBeShown() {
//Arrange
var fakePersonModel = new FakePerson() {
Name = string.Empty,
SelectionSubmitted = true
};
var controller = new HomeController();
//Act
controller.Index(fakePersonModel);
//Assert
var isShowSummarySetToTrue = fakePersonModel.ShowValidationSummary;
Assert.IsTrue(isShowSummarySetToTrue);
}
That aside, your model appears to be doing to much if the actual Get implementation does as you stated here
actual implementation return some value from the db
Consider refactoring that functionality out into a service (Single Responsibility Principle / Separation of Concerns)
public interface IPersonModelService {
string Get(PersonModel person);
}
public class PersonModelService : IPersonModelService {
public string Get(PersonModel person) {
//actual implementation return some value from the db
}
}
and keep the model as lean as possible. Also consider refactoring those public fields into public properties.
public class PersonModel {
public bool SelectionSubmitted { get; set; }
public bool ShowValidationSummary { get; set; }
public string Name { get; set; }
}
The controller would depend on the service abstraction
class HomeController : Controller {
private IPersonModelService service;
public HomeController(IPersonModelService service) {
this.service = service;
}
[HttpGet]
public ActionResult Index(PersonModel model) {
if (model.SelectionSubmitted && !ValidateSelections(model)) {
model.ShowValidationSummary = true;
}
return View("Index", service.Get(model));
}
private bool ValidateSelections(PersonModel model) {
if (model.Name == "") {
ModelState.AddModelError("EmptyPersonName", "Person name cannot be null");
}
return ModelState.IsValid;
}
}
And now the test can be exercised to completion in isolation.
[TestMethod]
public void GivenPerson_WhenSearchingForFutureBirthDate_ThenValidationMessageShouldBeShown() {
//Arrange
var model = new PersonModel() {
Name = string.Empty,
SelectionSubmitted = true
};
var serviceMock = new Mock<IPersonModelService>();
serviceMock.Setup(_ => _.Get(It.IsAny<PersonModel>())).Returns(string.Empty);
var controller = new HomeController(serviceMock.Object);
//Act
controller.Index(model);
//Assert
var isShowSummarySetToTrue = model.ShowValidationSummary;
Assert.IsTrue(isShowSummarySetToTrue);
}
I am trying to understand a behavior that is occurring in my application. I have mocked out my DbContext and when I make a call get items from the dbContext.Set<T>().ToList(), the second call does not contain my mocked data. I am not sure why this happens since that data should still exist. Please see code below:
SUT:
public class Employee
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public decimal Salary { get; set; }
}
public class EmployeeDb
: DbContext
{
public EmployeeDb()
{
}
public virtual IDbSet<Employee> Employees { get; set; }
}
UNIT TEST:
public class MockDatabase
{
public Mock<EmployeeDb> SetMockData()
{
var mockDb = new Mock<EmployeeDb>();
mockDb.Setup(i => i.Set<Employee>()).Returns(GetMockSet(Employees).Object);
mockDb.SetupGet(i => i.Employees).Returns(() => GetMockSet(Employees).Object);
return mockDb;
}
private List<Employee> _providers;
private List<Employee> Employees => _providers ?? (_providers = new List<Employee>
{
GetEmployee(1),
GetEmployee(2),
GetEmployee(3),
GetEmployee(4),
GetEmployee(5),
});
private static Employee GetEmployee(int id)
{
return new Employee
{
FirstName = Faker.Name.First(),
LastName = Faker.Name.Last(),
Age = Faker.RandomNumber.Next(50),
Id = id,
Salary = Faker.RandomNumber.Next(100000)
};
}
#region Hood
public static Mock<DbSet<T>> GetMockSet<T>(IList<T> items) where T : class
{
var querable = items.AsQueryable();
var mockSet = new Mock<DbSet<T>>();
mockSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(querable.Provider);
mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(querable.Expression);
mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(querable.ElementType);
mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(querable.GetEnumerator());
mockSet.Setup(i => i.Add(It.IsAny<T>())).Callback(delegate (T item) {
items.Add(item);
});
return mockSet;
}
#endregion
}
[TestClass]
public class UnitTest1
{
private EmployeeDb _context;
[TestInitialize]
public void TestInitialize()
{
var mockDb = new MockDatabase();
_context = mockDb.SetMockData().Object;
}
[TestMethod]
public void Test_CallTwice_ReturnsEqualCount()
{
var emps = _context.Set<Employee>().ToList();
var emps2 = _context.Set<Employee>().ToList();
Assert.IsTrue(emps.Count == emps2.Count);
// -- This works
//var empCount = _context.Set<Employee>().Count();
//var empCount2 = _context.Set<Employee>().Count();
//Assert.IsTrue(empCount == empCount2);
}
}
Is there something I am not getting about this code? Is there something Moq does with the ToList()?
ToList enumerates the set when invoked, but the enumerator is forward only, so after the first call it is already at the end.
When setting up the GetEnumerator use the function overload of the Returns in order to allow multiple calls other wise the same enumerator will be returned every time and you get the behavior you experienced.
mockSet.As<IQueryable<T>>()
.Setup(m => m.GetEnumerator())
.Returns(() => querable.GetEnumerator()); //<-- function