I have a function which based on some internal logic, stores a redirect string in a session variable (Redirect_To). I am trying to write a unit test which checks the value of this session variable once the action called has completed.
I am using Moq to mock up some of my properties and functions. However, whenever I set the Session variable (Redirect_To) up with Moq, it must set it as read only because I can no longer assign a value to it which leads my unit test to fail.
Here is my unit test:
public async Task Index_ClientTypeIsRPI_ExpectCorrectRedirect()
{
var sessionDict = new Dictionary<string, string>
{
{"CID", "124" },
{"UserName", "AK92630" },
{"REDIRECT_TO", "www.google.com" }
};
var controllerContext = HttpContextManager.ReturnMockControllerContextWithSessionVars(sessionDict);
var mockIdentity = new Mock<IIdentity>();
mockIdentity.Setup(x => x.Name).Returns("TestName");
controllerContext.Setup(x => x.HttpContext.User.Identity).Returns(mockIdentity.Object);
controllerContext.Setup(x => x.HttpContext.User.IsInRole(UserLevel.Subrep.GetDescription())).Returns(true);
controllerContext.Setup(x => x.HttpContext.Request.Browser.Browser).Returns("InternetExplorer");
_controller.ControllerContext = controllerContext.Object;
_iMockManifestManager.Setup(x => x.ReturnSingleMainDataRecordViaTransNoCIDAndUserNameAsync(It.IsAny<string>, It.IsAny<string>, It.IsAny<string>)).ReturnsAsync(New MainData());
var transNo = "Asdf";
await _controller.Index(transNo, true, "sd", "dg");
Assert.AreEqual("www.facebook.com", _controller.HttpContext.Session["REDIRECT_TO"]);
}
ReturnMockControllerContextWithSessionVars (Function which sets my session variables)
internal static Mock<ControllerContext> ReturnMockControllerContextWithSessionVars(Dictionary<string, object> sessionKeysAndValues):
{
var fakeHttpContext = HttpContextManager.CreateLocalAuthenticatedUserWithMockHttpContext();
var controllerContext = new Mock<ControllerContext>;
controllerContext.Setup(x => x.HttpContext).Returns(fakeHttpContext.Object);
foreach (var item in sessionKeysAndValues)
{
controllerContext.Setup(x => x.HttpContext.Session[item.Key]).Returns(item.Value);
}
return controllerContext;
}
Action:
public async Task Index(string transNo, bool? isRechase, string clientID, string clientType)
{
switch (clientType.ToUpper())
{
case "RPI":
Session["REDIRECT_TO"] = "www.reddit.com";
break;
case "LM":
Session["REDIRECT_TO"] = "www.bbc.co.uk";
default:
Session["REDIRECT_TO"] = "www.stackoverflow.com";
break;
}
//Do async stuff
return null;
}
Does anyone know how to change my code so when I setup Session["Redirect_To"] it remains read/write?
Also, I have converted this from VB.NET but I am fluent in both languages so if I have made any syntax errors it's from writing it free-hand. If that's the case, just let me know.
Thanks
Create a Stub to use for the session as moq will have difficulty setting up the collection used to hold values in the indexed property. The other dependencies should work as expected with moq.
class HttpSessionStateMock : HttpSessionStateBase {
private readonly IDictionary<string, object> objects = new Dictionary<string, object>();
public HttpSessionStateMock(IDictionary<string, object> objects = null) {
this.objects = objects ?? new Dictionary<string, object>();
}
public override object this[string name] {
get { return (objects.ContainsKey(name)) ? objects[name] : null; }
set { objects[name] = value; }
}
}
Using the example method under test from your original question base on "RPI" being the clientType the Following Test was exercised to completion and passed as expected.
[TestMethod]
public async Task Index_ClientTypeIsRPI_ExpectCorrectRedirect() {
//Arrange
var mock = new MockRepository(MockBehavior.Loose) {
DefaultValue = DefaultValue.Mock,
};
var session = new HttpSessionStateMock();//<-- session stub
var controllerContext = mock.Create<ControllerContext>();
controllerContext.Setup(_ => _.HttpContext.Session).Returns(session);
var _controller = new MyController() {
ControllerContext = controllerContext.Object
};
var transNo = "Asdf";
var clientID = "123";
var clientType = "RPI";
var sessionKey = "REDIRECT_TO";
var expected = "www.reddit.com";
//Act
await _controller.Index(transNo, true, clientID, clientType);
var actual = _controller.HttpContext.Session[sessionKey].ToString();
//Assert
Assert.AreEqual(expected, actual);
}
Related
Here is my code:
public IActionResult Post([FromBody] ApiPermission apiClient)
{
return Ok(_apiPermissionService.Add(apiClient, GetCurrentUserFullName(_httpContextAccessor.HttpContext.User)));
}
I need the result of GetCurrentUserFullName which has the definition of
internal string GetCurrentUserFullName(ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
return principal.FindFirstValue("UserFullName");
}
How we can pass GetCurrentUserFullName to pass the test.
Below is my testing code:
public void Post()
{
Mock<ApiPermissionGroup> mockApiPermissionGroup = new Mock<ApiPermissionGroup>();
var apiPermission = new ApiPermission
{
ApiPermissionId = 1,
Name = "Name",
Description = "Description",
AddedBy = "AddedBy",
AddedDate = DateTime.UtcNow,
ModifiedBy = "ModifiedBy",
ModifiedDate = DateTime.UtcNow,
ApiPermissionGroupId = 2,
ApiPermissionGroup = mockApiPermissionGroup.Object
};
List<ApiPermission> lstApiPermission = new List<ApiPermission>();
lstApiPermission.Add(apiPermission);
ApiPermissionController ApiPermissionController = new ApiPermissionController(_mockApiPermissionService.Object, _mockHttpContextAccessor.Object);
var result = ApiPermissionController.Post(apiPermission);
Assert.IsNotNull(result);
}
When running the above code I am getting the error " System.NullReferenceException : Object reference not set to an instance of an object." at
Microsoft.AspNetCore.Http.IHttpContextAccessor.HttpContext.get returned null.
In this specific case, I'd not mock the internal method, but mock the context accessor, e.g.:
// Set up mock principal
var principal = new Mock<ClaimsPrincipal>();
principal
.Setup(r => r.FindFirst("UserFullName"))
.Returns(new Claim("UserFullName", "TEST_USER_NAME"));
// Create dummy context
var context = new DefaultHttpContext { User = principal.Object };
// Set up mock context accessor
var mockCtxAcc = new Mock<IHttpContextAccessor>();
mockCtxAcc.SetupGet(x => x.HttpContext).Returns(context);
// Create controller under test
var ctrl = new MyController(mockCtxAcc.Object, ...);
The unit test should test the behavior of the class as it is, but without the dependencies of the class. This way, you test the code that the controller runs in real life scenario - also the internal method as it is.
I am using Microsoft Graph Api client and performing add members to group.
Docs here :- https://learn.microsoft.com/en-us/graph/api/group-post-members?view=graph-rest-1.0&tabs=csharp#example-2-add-multiple-members-to-a-group-in-a-single-request
I have successfully achieved the requirement. But when come to write test for my service class having no clue what and how to verify it.
I am beginner to the API dev and also with Microsoft Graph API. Below are my codes, Take a look and post your suggestions and comments. It might be helpful.
Service Class:
public class UserGroupService : IUserGroupService
{
private readonly IGraphServiceClient _graphServiceClient;
public UserGroupService(IGraphServiceClient graphServiceClient)
{
_graphServiceClient = graphServiceClient;
}
public async Task AddAsync(string groupId, IList<string> userIds)
{
var group = new Group
{
AdditionalData = new Dictionary<string, object>()
{
{"members#odata.bind", userIds.Select(x => $"https://graph.microsoft.com/v1.0/directoryObjects/{x}") }
}
};
await _graphServiceClient.Groups[groupId].Request().UpdateAsync(group);
}
}
ServiceTest :
public class UserGroupServiceTests
{
private readonly Fixture _fixture = new Fixture();
private readonly Mock<IGraphServiceClient> _graphServiceClientMock = new Mock<IGraphServiceClient>();
private readonly IUserGroupService _userGroupService;
public UserGroupServiceTests()
{
_userGroupService = new UserGroupService(_graphServiceClientMock.Object);
}
// Settingup GraphClientMock
private void SetupGraphClientMock(string groupId, IList<string> userIds, Group group)
{
var groupRequest = new Mock<IGroupRequest>();
var groupRequestBuilder = new Mock<IGroupRequestBuilder>();
groupRequest.Setup(x => x.UpdateAsync(group));
groupRequestBuilder.Setup(x => x.Request()).Returns(groupRequest.Object);
_graphServiceClientMock.Setup(x => x.Groups[groupId]).Returns(groupRequestBuilder.Object);
}
[Fact]
public async Task AddAsync_GivenValidInput_WhenServiceSuccessful_AddAsyncCalledOnce()
{
object result;
var groupId = _fixture.Create<string>();
var userIds = _fixture.Create<IList<string>>();
var dictionary = _fixture.Create<Dictionary<string, object>>();
dictionary.Add("members#odata.bind", userIds.Select(x => $"https://graph.microsoft.com/v1.0/directoryObjects/{x}"));
var group = _fixture.Build<Group>().With(s => s.AdditionalData, dictionary).OmitAutoProperties().Create();
SetupGraphClientMock(groupId, userIds, group);
await _userGroupService.AddAsync(groupId, userIds);
//TODO
// Need to verify _graphServiceClientMock AdditionalData value == mocking group AdditionalData value which is called once in _graphServiceClientMock.
// Below implementation done using TryGetValue which return bool, I am really afraid to write test using bool value and compare and I feel its not a right way to write test.
_graphServiceClientMock.Verify(m => m.Groups[groupId].Request().UpdateAsync(It.Is<Group>(x => x.AdditionalData.TryGetValue("members#odata.bind", out result) == group.AdditionalData.TryGetValue("members#odata.bind", out result))), Times.Once);
_graphServiceClientMock.VerifyNoOtherCalls();
}
}
I wants to verify _graphServiceClientMock AdditionalData value == mocking group AdditionalData value which is called once in _graphServiceClientMock like the above. Anyone have idea on this. Please post your comments.Thanks in Advance.
Based on the subject under test and the simplicity of the provided member under test the following example demonstrates how it can be tested in isolation,
public class UserGroupServiceTests {
[Fact]
public async Task AddAsync_GivenValidInput_WhenServiceSuccessful_AddAsyncCalledOnce() {
//Arrange
string groupId = "123456";
IList<string> userIds = new[] { "a", "b", "c" }.ToList();
string expectedKey = "members#odata.bind";
IEnumerable<string> expectedValues = userIds
.Select(x => $"https://graph.microsoft.com/v1.0/directoryObjects/{x}");
Group group = null;
Mock<IGraphServiceClient> clientMock = new Mock<IGraphServiceClient>();
clientMock
.Setup(x => x.Groups[groupId].Request().UpdateAsync(It.IsAny<Group>()))
.Callback((Group g) => group = g) //Capture passed group for assertion later
.ReturnsAsync(group) //To allow async flow
.Verifiable();
IUserGroupService _userGroupService = new UserGroupService(clientMock.Object);
//Act
await _userGroupService.AddAsync(groupId, userIds);
//Assert
clientMock.Verify(); //have verifiable expressions been met
clientMock.VerifyNoOtherCalls();
//Using FluentAssertions to assert captured group
group.Should().NotBeNull();//was a group passed
group.AdditionalData.Should().NotBeNull()// did it have data
.And.ContainKey(expectedKey);//and did the data have expected key
(group.AdditionalData[expectedKey] as IEnumerable<string>)
.Should().BeEquivalentTo(expectedValues);//are values as expected
}
}
Review the code comments to get an understanding of how the test was exercised to verify the expected behavior.
The intuitive nature of the used FluentAssertions should also help in understanding what is being asserted
I have code snippet as following. Although setup with retrurn true but value always return false.
Could someone advise how to retrieve value as true?
public interface IDatabaseService
{
bool ProcessSQL(INApplicationProcessDTO inApplicationProcessDTO, string connectionString, string storedProcedureName);
bool CompleteRun(INApplicationProcessDTO inApplicationProcessDTO, string connectionString, string storedProcedureName);
}
----CLASS----------
public static class INHelper
{
public static bool CompleteRun(INApplicationProcessDTO inApplicationProcessDTO, string connectionString = null, IDatabaseService databaseService = null)
{
if (inApplicationProcessDTO == null)
{
return false;
}
if (inApplicationProcessDTO.Data == null)
{
return false;
}
const string storedProcedureName = "PSP_PWS_INApplication_Application_Process_CompleteRun";
// Get Connection String to Parity4 Database from Parity4 WebService Web.config
if (connectionString == null)
{
if (ConfigurationManager.ConnectionStrings["ConnectionString"] != null)
{
connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
}
}
if (string.IsNullOrWhiteSpace(connectionString))
{
return false;
}
if (databaseService == null)
{
databaseService = new DatabaseService();
}
//Why always return false line below
return databaseService.CompleteRun(inApplicationProcessDTO, connectionString, storedProcedureName);
}
}
----TEST METHOD----
public void When_CompleteRun_ConnectionValid_Expect_True()
{
var iNApplicationProcessDTOTest = new INApplicationProcessDTO()
{
Data = new System.Xml.Linq.XDocument(),
ErrorCount = 0,
Errors = ""
};
Mock<IDatabaseService> iDatabaseService = null;
iDatabaseService = new Mock<IDatabaseService>();
iDatabaseService.Setup(t => t.CompleteRun(iNApplicationProcessDTOTest, "test", "test")).Returns(true);
iDatabaseService.Setup(t => t.ProcessSQL(iNApplicationProcessDTOTest, "test", "test")).Returns(true);
var iNApplicationProcessDTO = new INApplicationProcessDTO()
{
Data = new System.Xml.Linq.XDocument(),
ErrorCount = 0,
Errors = ""
};
var actual = INHelper.CompleteRun(iNApplicationProcessDTO, "a", iDatabaseService.Object);
//actual always false although Returns(true) in Setup
Assert.AreEqual(true, actual);
}
Appreciate your help.
There are a couple of tiny problems with your Test code.
First let me show how you should implement it and then let me give you some explanation:
[Fact]
public void GivenAFlawlessDatabaseService_WhenICallCompleteRun_ThenItCallsTheUnderlyingServicesCompleteRun()
{
//Arrange
const string connectionString = "a";
var iDatabaseService = new Mock<IDatabaseService>();
iDatabaseService
.Setup(db => db.CompleteRun(It.IsAny<INApplicationProcessDTO>(), It.IsAny<string>(), It.IsAny<string>()))
.Returns(true);
var iNApplicationProcessDTO = new INApplicationProcessDTO
{
Data = new XDocument(),
ErrorCount = 0,
Errors = ""
};
//Act
var actual = INHelper.CompleteRun(iNApplicationProcessDTO, connectionString, iDatabaseService.Object);
//Assert
Assert.True(actual);
iDatabaseService.Verify(db => db.CompleteRun(iNApplicationProcessDTO, connectionString, "PSP_PWS_INApplication_Application_Process_CompleteRun"), Times.Once);
iDatabaseService.Verify(db => db.ProcessSQL(It.IsAny<INApplicationProcessDTO>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never);
}
First, I would suggest to use somewhat more meaningful names for your tests. Here I've used a Given When Then structure to describe under what circumstances (given) if a specific action is triggered (when) what would I expect (then)
I also suggest to use the Arrange, Act and Assert comments to separate different phases of your test case visually
The Setup method calls normally do not rely on concrete objects. The reason behind it is that classes are compared by reference. So, even if you have two seemingly identical instances (like: iNApplicationProcessDTOTest and iNApplicationProcessDTO) they are different. That's why the mock function is not being called.
Use It.IsAny<T>() calls in the Setup phase and use concrete values in the Verify phase.
If one of the methods of the mocked object is not called by the SUT (system under test) then you don't need to Setup it.
But you can still verify that it has not been called (Verify(..., Times.Never);)
Brand new both to .net core as well as for unit testing, so please be at my help pointing out where the problem lies in my code.
[TestMethod]
public void RetrieveAsync_AddModel_ReturnsNotEmptyPersonList()
{
// Arrange
var data = new List<Person>();
var mySet = new Mock<DbSet<Person>>();
var defaultHttpContext = new DefaultHttpContext();
var httpContextAccessorMock = new Mock<IHttpContextAccessor>();
httpContextAccessorMock.Setup(m => m.HttpContext).Returns(defaultHttpContext);
var myContextMock = new Mock<MyContext>(new DbContextOptions<MyContext>(), httpContextAccessorMock.Object);
myContextMock.Setup(c => c.Set<Person>()).Returns(mySet.Object);
var id = Guid.NewGuid();
var service = new PersonService(myContextMock.Object);
var model = new Person
{
Id = id
};
// Act
var createdModel = service.CreateAsync(model).Result;
data.Add(createdModel);
var findModelById = data.Find(item => item.Id == createdModel.Id).Id;
**var retrievedModel = service.RetrieveAsync(findModelById).Result; // null**
// Assert
**Assert.AreEqual(id, createdModel.Id); // correct, test passes
Assert.AreEqual(id, retrievedModel.Id); // retrievedModel is null, test fails**
}
Generics methods I have to test are as follows:
public async Task<T> CreateAsync(T model)
{
_dbContext.Set<T>().Add(model);
await _dbContext.SaveChangesAsync();
return model;
}
public async Task<T> RetrieveAsync(Guid id)
{
return await _dbContext.Set<T>().FindAsync(id);
}
The question is, why retrievedModel is null?
I have the following three methods in the CompanyApplication class (along with the supporting factories and services listed):
public ResultSet<CompanyDto> AddCompany(CompanyDto companyDto)
{
var result = new CompanyDto();
var company = new Company();
Mapper.Map(companyDto, company);
using (ITransaction t = _transactionFactory.Create())
{
company = _companyService.Add(company);
t.Commit();
}
Mapper.Map(company, result);
return new ResultSet<CompanyDto>(1, new[] { result });
}
public ResultSet<CompanyContactDto> AddCompanyContact(CompanyContactDto companyContactDto)
{
var result = new CompanyContactDto();
var company = new Company();
var contact = new CompanyContact();
Mapper.Map(companyContactDto, contact);
using (ITransaction t = _transactionFactory.Create())
{
var contactCompanies = FindByIdJoin<Company, CompanyDto>(companyContactDto.CompanySK);
Mapper.Map(contactCompanies.Data.First(), company);
company.CompanyContacts.Add(contact);
company = _companyService.Update(company);
t.Commit();
}
Mapper.Map(contact, result);
return new ResultSet<CompanyContactDto>(1, new[] { result });
}
public ResultSet<T_DtoType> FindByIdJoin<T_DbType, T_DtoType>(long id)
{
IAbstractRepository<T_DbType> repository = EnsureRepository<T_DbType>();
T_DbType entity = repository.FindByIdJoin(id);
return (entity == null ? null : MapResultSetToDto<T_DbType, T_DtoType>(entity));
}
There are other objects in play here, which is why the FindByIdJoin has been made a separate method in the CompanyApplication class.
I have set up the testing class with some mocks and an instance of the CompanyApplication class:
private Mock<ICompanyRepository> _mockCompanyRepository;
private Mock<ICompanyDomain> _mockCompanyService;
private Mock<ITransactionFactory> _mockTransactionFactory;
private Mock<ITransaction> _mockTransaction;
private CompanyApplication _companyApplication;
[Setup]
public void SetUp()
{
_mockCompanyRepository = new Mock<ICompanyRepository>(MockBehavior.Strict);
_mockCompanyService = new Mock<ICompanyDomain>(MockBehavior.Strict);
_mockTransactionFactory = new Mock<ITransactionFactory>(MockBehavior.Strict);
_mockTransaction = new Mock<ITransaction>(MockBehavior.Strict);
_companyApplication = new CompanyApplication(
_mockCompanyRepository.Object,
_mockCompanyService.Object,
_mockTransactionFactory.Object);
}
I am successfully able to test the FindByIdJoin and AddCompany methods directly in Moq like this:
[Test]
public void CanFindCompanyByIdJoin()
{
var data = new Company {ObjectId = 1, Name = "Company1"};
_mockCompanyRepository.Setup(x => x.FindByIdJoin(It.Is<long>(arg => arg == data.ObjectId)))
.Returns(data);
var result = _companyApplication.FindByIdJoin<Company, CompanyDto>(data.ObjectId);
Assert.AreEqual(data.ObjectId, result.Data.First().ObjectId);
}
[Test]
public void CanAddCompany()
{
var data = new Company {ObjectId = 1, Name = "Company1"};
_mockCompanyService.Setup(x => x.Add(It.Is<Company>(arg => arg.ObjectId == data.ObjectId)))
.Returns(data);
_mockTransactionFactory.Setup(x => x.Create()).Returns(_mockTransaction.Object);
_mockTransaction.Setup(x => x.Commit());
_mockTransaction.Setup(x => x.Dispose());
var dto = new CompanyDto {ObjectId = 1, Name = "Company1"};
var result = _companyApplication.AddCompany(dto);
_mockCompanyService.Verify(t => t.Add(It.IsAny<Company>()));
}
Those two tests pass just fine. However, I'm having trouble coming up with a test for AddCompanyContact, because it calls FindByIdJoin as part of its flow, and that seems to be getting in the way.
Specifically, is there a way to mock var contactCompanies = FindByIdJoin<Company, CompanyDto>(companyContactDto.CompanySK) in a test for the AddCompanyContact method?
Thanks!
There is two alternatives that i see depending on the amount of work that you want to do.
Wrap that call into a object and instantiate it using a IOC container. This is the one that i feel would take the most effort if you are not using one already.
Turn that call into a Func and make a method without that parameter that does the call. This approach has the disadvantage that the top call will be untestable but will allow access to the rest of the method.
Example Below:
public ResultSet<CompanyContactDto> AddCompanyContact(CompanyContactDto companyContactDto)
{
AddCompanyContact(CompanyContactDto, ()=>
{
return FindByIdJoin<Company, CompanyDto> companyContactDto.CompanySK);
}
}
public ResultSet<CompanyContactDto> AddCompanyContact(CompanyContactDto companyContactDto, Func<WhateverTheMethodReturns> findIdReplacement)
{
var result = new CompanyContactDto();
var company = new Company();
var contact = new CompanyContact();
Mapper.Map(companyContactDto, contact);
using (ITransaction t = _transactionFactory.Create())
{
var contactCompanies = findIdReplacement();
Mapper.Map(contactCompanies.Data.First(), company);
company.CompanyContacts.Add(contact);
company = _companyService.Update(company);
t.Commit();
}
Mapper.Map(contact, result);
return new ResultSet<CompanyContactDto>(1, new[] { result });
}
I was over complicating the problem... since AddCompanyContact calls FindByIdJoin, all I needed to do was mock the same interface that is used in FindByIdJoin.
Lesson learned: mock interfaces, not classes.