Good day everyone,
I'm new in xunit and even in unit testing. I have a code here and I'm trying to assert two collection of list. But I have no idea how to assert and pass this test. Here's my code
[Theory]
[InlineData(1)]
public void GetAllStudents_Exempt1(int number)
{
// arrange
var studentRepo = new Mock<IStudentRepository>();
var listOfStudents = new List<Student> { new Student { StudentId = 1, Firstname = "Firstname1", Lastname = "Firstname1" },
new Student{StudentId=2, Firstname="Firstname2",Lastname="Lastname2"} };
var getAllStudentDetailsExempt1 = studentRepo.Setup(s => s.GetStudents()).Returns(listOfStudents.Where(x => x.StudentId != number));
var studentService = new StudentService(studentRepo.Object);
// act
var getStudentsDetails = studentService.ListOfStudentsExempt1(1);
// assert
// I don't have any idea how to assert
}
First a few notes:
var getAllStudentDetailsExempt1 = studentRepo
.Setup(s => s.GetStudents())
.Returns(listOfStudents.Where(x => x.StudentId != number));
you don't need var getAllStudentDetailsExempt1, you can just setup your repo-mock...
studentRepo
.Setup(s => s.GetStudents())
.Returns(listOfStudents.Where(x => x.StudentId != number));
You probably want to change:
// act
var getStudentsDetails = studentService.ListOfStudentsExempt1(1);
to use the number variable...
// act
var getStudentsDetails = studentService.ListOfStudentsExempt1(number);
So then you can assert by checking some properties:
Assert.Equals(1, getStudentsDetails.Count);
Assert.Equals("FirstName1", getStudentsDetails.First().Firstname);
etc. etc.
Give it a shot!
Related
I've added some unit tests to an ASP.NET MVC app I've written using .NET 6. I'm using an in-memory database for the unit tests. One of the unit tests fails with the following error:
Xunit.Sdk.IsAssignableFromException HResult=0x80131500
Message=Assert.IsAssignableFrom() Failure Expected:
typeof(System.Collections.Generic.List<PharmacyWarehouseV2.Models.SiteToSite>)
Actual: (null) Source=xunit.assert StackTrace: at
Xunit.Assert.IsAssignableFrom(Type expectedType, Object object) at
Xunit.Assert.IsAssignableFrom[T](Object object) at
PharmacyWarehouseV2.Test.SiteToSiteControllerTest.IndexTest() in
D:\Repos\PW\PharmacyWarehouseV2\PharmacyWarehouseV2.Test\SiteToSiteControllerTest.cs:line
29
The strange thing is I've got another unit test I wrote in the same way, which works fine.
Here is the code for the in-memory data I'm using with Moq (note: the SiteToSite class is large, so I'll be removing most of the properties for brevity's sake. The only properties which are required are the first two):
public static IEnumerable<SiteToSite?> GetSiteToSites()
{
// Construct some SiteToSites first
var siteToSites = new List<SiteToSite>()
{
new SiteToSite()
{
SiteToSiteID = 1,
DateReceived = new DateTime(2020, 7, 1),
OrderedByName = "John Doe",
OrderedByID = 1,
// other properties removed for brevity
},
new SiteToSite()
{
SiteToSiteID = 2,
DateReceived = new DateTime(2021, 3, 1),
OrderedByName = "Teresa",
OrderedByID = 2,
// other properties removed for brevity
}
};
// Now construct SiteToSiteItems
var ss1 = new SiteToSiteItem()
{
SiteToSiteItemID = 1,
SiteToSiteID = 1,
ProgramName = "Program One",
Notes = "First note"
};
var ss2 = new SiteToSiteItem()
{
SiteToSiteItemID = 2,
SiteToSiteID = 2,
ProgramName = "Program Two",
Notes = "Second note"
};
var ss3 = new SiteToSiteItem()
{
SiteToSiteItemID = 3,
SiteToSiteID = 2,
ProgramName = "Program Two",
Notes = "Third note"
};
// Now assing SiteToSiteItems to their parent SiteToSites
siteToSites[0].SiteToSiteItems = new List<SiteToSiteItem>() { ss1 };
siteToSites[1].SiteToSiteItems = new List<SiteToSiteItem>() { ss2, ss3 };
return siteToSites;
}
I use a service/repository class. This is the method that is used in the unit test:
public IEnumerable<SiteToSite?> GetAll()
{
var tmp = _context.SiteToSite.OrderBy(s => s.SiteToSiteID);
return tmp;
}
And here's the unit test that's failing:
[Fact]
public void IndexTest()
{
// arrange
var mockRepo = new Mock<ISiteToSiteService>();
mockRepo.Setup(m => m.GetAll()).Returns(SiteToSiteMockData.GetSiteToSites());
var emptyDbContext = new PharmacyDBContext(); //won't test the AJAX calls
var controller = new SiteToSiteController(emptyDbContext, mockRepo.Object);
// act
var result = controller.Index();
// assert
Assert.NotNull(result);
var viewResult = Assert.IsType<ViewResult>(result);
Assert.True(viewResult.ViewData.Count > 0, "viewResult does not have any records, as it should");
var viewResultSites = Assert.IsAssignableFrom<List<SiteToSite>>(viewResult.ViewData.Model);
if (viewResultSites.Count > 0)
{
// NOTE: I do not like this; it violates unit testing.
Assert.Equal(2, viewResultSites.Count);
Assert.Equal("John Doe", viewResultSites[0]?.OrderedByName);
}
}
When I debug the test, after the result variable is assigned in the "act" step, it does have the data from in-memory. However, the viewResult.ViewData.Model is null. I don't understand how result has data, but viewResult.ViewData.Model doesn't. I've gone to the xUnit repo on GitHub to look at the documentation, but it wasn't clear to me what the problem is. What might be causing the discrepancy?
Addendum 1
Here's the GetAll() method from the SiteToSiteService:
public IEnumerable<SiteToSite?> GetAll()
{
var tmp = _context.SiteToSite.OrderBy(s => s.SiteToSiteID);
return tmp;
}
And here's the Index() method from the controller:
public IActionResult Index()
{
return View();
}
(Posting answer on behalf of the question author, to move it to the answer space).
After Peter's question I realized I made a mistake. The Index action method didn't pass any data into the view. This is what was giving me a null. (For business reasons we don't show a list of site-to-sites.) So, I've put an xUnit [Fact(Skip = "<message>")] into the unit test for the IndexTest.
However, I do have a DetailTest unit test. Here's the code for the DetailTest:
[Fact]
public void DetailsTest()
{
// arrange
var mockRepo = new Mock<ISiteToSiteService>();
mockRepo.Setup(m => m.Get(1)).Returns(SiteToSiteMockData.Get(1));
var controller = new SiteToSiteController(new PharmacyDBContext(), mockRepo.Object);
// act
var result = controller.GetSiteToSiteById(1);
// assert
var viewResult = Assert.IsType<ViewResult>(result);
var viewResultSiteToSite = Assert.IsAssignableFrom<SiteToSite>(viewResult.ViewData.Model);
Assert.Equal("John Doe", viewResultSiteToSite.OrderedByName);
}
Here's the action method from the controller:
public ActionResult GetSiteToSiteById(int id)
{
var site2Site = siteToSiteService.Get(id);
if (site2Site == null)
{
return NotFound();
}
return View("SiteToSiteDetail", site2Site);
}
and here's the Get(int id) method from the service/repository method:
public SiteToSite? Get(int id)
{
var result = _context.SiteToSite.Include(i => i.SiteToSiteItems).Where(s => s.SiteToSiteID == id);
return result.FirstOrDefault();
}
I have the following test code.
var test = "Test";
var command = new MyCommand { V = test };
var mock = new Mock<IRepository>(); // IRepository has the method of Save()
var p = new P(test);
mock.Setup(x => x.Save(p)).Verifiable();
var sut = new C(mock.Object);
var result = await sut.M(command);
mock.Verify();
The test should pass. However, it failed with the error of,
Message:
Moq.MockException : Mock:
This mock failed verification due to the following:
IRepository x => x.Save(P):
This setup was not matched.
Stack Trace:
Mock.Verify()
sut.M() will convert a string X to type P with value of P(X).
It seems to me that you want to verify that the Save method from your mock is called with a specific value, and not just a type.
I have tried something like the following and believe it should work. I have modified your example.
var test = "Test";
var command = new MyCommand { V = test };
var mock = new Mock<IRepository>(); // IRepository has the method of Save()
var p = new P(test);
mock.Setup(x => x.Save(It.IsAny<P>());
var sut = new C(mock.Object);
var result = await sut.M(command);
mock.Verify(x => x.Save(It.Is<P>(v => v.Value.Equals(p.Value))), Times.AtLeastOnce);
This tests that the values of the specific property are equal.
I Tested this with the following test:
var test = "Test";
var mock = new Mock<ITestRepository>(); // ITestRepository has the method of Save()
var p = new P(test);
mock.Setup(x => x.Save(It.IsAny<P>()));
mock.Object.Save(new P(test));
mock.Verify(x => x.Save(It.Is<P>(v => v.Value.Equals(p.Value))), Times.AtLeastOnce);
I implement unit tests for the engine and setup 2 different methods in the repository mock. So the first one works well, but when I do linq Select for the second one it returns 0, whereas I did setup to return specific object.
My code in Engine:
private readonly IEmployerWorkersClient _employerWorkersClient;
private readonly IJobsClient _jobsClient;
public EmployerWorkerEngine(IEmployerWorkersClient employerWorkersClient, IJobsClient jobsClient,)
{
_employerWorkersClient = employerWorkersClient;
_jobsClient = jobsClient;
}
public async Task<Grid<WorkerFiltered>> GetWorkersAsync(int employerId, GridState gridState)
{
var employerWorkers = await _employerWorkersClient.GetEmployerWorkersByEmployerIdAsync(employerId);
int? payrollId = null;
int? jobRoleId = null;
DateTime? bookedStart = null;
// !!!the following result is Empty collection!!!
List<JobRoleExtended> jobRoles = (await _jobsClient.GetJobRoleExtendedByEmployerWorkerIdsAsync(employerWorkers.Select(ew => ew.Id), payrollId, jobRoleId, bookedStart)).ToList();
// Other things
}
And hereafter my unit test class
private readonly EmployerWorkerEngine _employerWorkerEngine;
private readonly Mock<IEmployerWorkersClient> _employerWorkersClientMock;
private readonly Mock<IJobsClient> _jobClientMock;
public WorkersFilterTests()
{
_employerWorkersClientMock = new Mock<IEmployerWorkersClient>();
_jobClientMock = new Mock<IJobsClient>();
_employerWorkerEngine = new EmployerWorkerEngine(_employerWorkersClientMock.Object, _jobClientMock.Object,);
}
[Theory]
[InlineData(1)]
public async Task GetFilteredWorkersByEmployerIdSuccessSimple(int employerId)
{
// Arrange
const int employerWorkerId = 3;
var gridState = new GridState { Skip = 0, Take = 1 };
var employerWorkers = new List<EmployerWorker> { new EmployerWorker {EmployerId = 1, WorkerId = 2, Id = employerWorkerId} };
_employerWorkersClientMock.Setup(client => client.GetEmployerWorkersByEmployerIdAsync(employerId))
.ReturnsAsync(() => employerWorkers);
var jobRolesExtended = new List<JobRoleExtended>
{
new JobRoleExtended
{
EmployerWorkerId = employerWorkerId,
BookedStartDate = DateTime.UtcNow,
BookedEndDate = DateTime.UtcNow.AddDays(1),
Id = 5,
JobId = 8,
Name = "Job 5",
PayrollId = 10,
PayrollName = "Conduct"
}
};
_jobClientMock
.Setup(client => client.GetJobRoleExtendedByEmployerWorkerIdsAsync(employerWorkers.Select(ew => ew.Id), null, null, null))
.ReturnsAsync(() => jobRolesExtended);
}
So, during testing the following method returns Empty collection:
_jobsClient.GetJobRoleExtendedByEmployerWorkerIdsAsync() and I don't get why.
I assume that it related with the reference of the collection that generates linq Select, but even in this case, I don't know how to handle it for testing.
Can somebody help me with that?
does this help:
_jobClientMock.Setup(client => client.GetJobRoleExtendedByEmployerWorkerIdsAsync(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>())
.ReturnsAsync(() => jobRolesExtended);
(do check if I got the types right)
if this is the case, then your missing something with your parameters.
With Moq you have to setup the expectations correctly or else when called the mock wont match and thus not perform as expected.
In the method under test you have
int? payrollId = null;
int? jobRoleId = null;
DateTime? bookedStart = null;
List<JobRoleExtended> jobRoles =
(await _jobsClient.GetJobRoleExtendedByEmployerWorkerIdsAsync(
employerWorkers.Select(ew => ew.Id), payrollId, jobRoleId, bookedStart)
)
.ToList();
The Select statement appears to be returning a collection of Ids (IEnumerable<int>) and you have already configured the first call correctly.
You now need to correctly configure the second call to expect that collection of int Ids
_jobClientMock
.Setup(_ => _.GetJobRoleExtendedByEmployerWorkerIdsAsync(
It.IsAny<IEnumerable<int>>(),
null,
null,
null)
)
.ReturnsAsync(() => jobRolesExtended);
My Moq is returning data I don't expect.
var artist = new Artist();
artist.Id = "4576";
var deserializer = Mock.Of<IXmlDeserializer<Album>>(
d => d.Deserialize("foo").Artist == artist);
Assert.IsNull(deserializer.Deserialize(null));
The above test fails.
The really puzzling thing is that the mock returns a Mock but that
Assert.AreEqual("4576", deserializer.Deserialize(null).Artist.Id)
returns true.
It's as if the "default" returned by my mock for an unspecified argument (in this case null) was somehow influenced by what I told it to return when called with "foo".
You can either write:
var artist = new Artist { Id = "4576" };
var mock = new Mock<IXmlDeserializer<Album>>();
mock.Setup(x => x.Deserialize(It.Is<string>(i => i == "foo"))).Returns(new Album() { Artist = artist });
var deserializer = mock.Object;
Assert.IsNull(deserializer.Deserialize(null));
Assert.IsNotNull(deserializer.Deserialize("foo"));
Or using the Mock.Of() syntax the above would be:
var artist = new Artist { Id = "4576" };
var deserializer = Mock.Of<IXmlDeserializer<Album>>(d => d.Deserialize(It.Is<string>(i => i == "foo")) == Mock.Of<Album>(album => album.Artist == artist));
Assert.IsNull(deserializer.Deserialize(null));
Assert.IsNotNull(deserializer.Deserialize("foo"));
Assert.AreEqual("4576", deserializer.Deserialize("foo").Artist.Id);
I have a unit test done using moq to mock the objects, and the test is working fine, and now I want to use autofac +moq, but I'm having a few problems.
this is the test:
using (var mock = AutoMock.GetLoose())
{
var issues = new List<Issue>();
issues.Add(new Issue { Organization = "org", Repository = "repo", Number = 1 });
issues.Add(new Issue { Organization = "org", Repository = "repo", Number = 2 });
var numKeys = 0;
mock.MockRepository.Create<IStorageService>()
.Setup(myMock => myMock.GetBatchIssues(It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<IList<string>>()))
.Callback((string org, string repo, IList<string> keys) => numKeys = keys.Count)
.Returns(issues);
var sut = mock.Create<IssueReceiveService>();
var check = await sut.CheckInStorage("org", "repo", issues);
Assert.AreEqual(issues.Count, numKeys);
}
the call to sut.CheckInStorage return null, and the variable numKeys is not updated to the correct value. This test works fine using just moxk, so I suppose I'm missing something how to configure a mock with autoMock.
Where can I find more informations?
UPDATE:
after a few more tests I found the solution
using (var mock = AutoMock.GetLoose())
{
var issues = new List<Issue>();
issues.Add(new Issue { Organization = "org", Repository = "repo", Number = 1 });
issues.Add(new Issue { Organization = "org", Repository = "repo", Number = 2 });
var numKeys = 0;
mock.Mock<IStorageService>()
.Setup(myMock => myMock.GetBatchIssues(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IList<string>>()))
.Callback((string org, string repo, IList<string> keys) => numKeys = keys.Count)
.Returns(issues);
var sut = mock.Create<IssueReceiveService>();
var check = await sut.CheckInStorage("org", "repo", issues);
Assert.AreEqual(issues.Count, numKeys);
}
after a few more tests I found the solution
using (var mock = AutoMock.GetLoose())
{
var issues = new List<Issue>();
issues.Add(new Issue { Organization = "org", Repository = "repo", Number = 1 });
issues.Add(new Issue { Organization = "org", Repository = "repo", Number = 2 });
var numKeys = 0;
mock.Mock<IStorageService>()
.Setup(myMock => myMock.GetBatchIssues(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IList<string>>()))
.Callback((string org, string repo, IList<string> keys) => numKeys = keys.Count)
.Returns(issues);
var sut = mock.Create<IssueReceiveService>();
var check = await sut.CheckInStorage("org", "repo", issues);
Assert.AreEqual(issues.Count, numKeys);
}