unit testing with mock - c#

I have to unit test a function (SendMessageNetSkyToAgenceGrp), and inside this function there is a service that I want to mock (EnvoyerNotificationSms.SendMessageNetSky(signalRMessage)):
Here is the code i want to test :
public void SendMessageNetSkyToAgenceGrp(
int agenceId,
string message,
string name,
bool addAgenceLibInMsg = true,
SignalRMessageThemeEnum theme = SignalRMessageThemeEnum.Information)
{
Agence agence = AgenceCoreService.GetAgenceById(agenceId);
if (agence == null || (string.IsNullOrEmpty(agence.Libelle) && addAgenceLibInMsg))
{
return;
}
string finalMessage = (message + (addAgenceLibInMsg ? agence.Libelle : ""));
string groupName = string.Empty;
if (theme == SignalRMessageThemeEnum.NotificationSms)
{
groupName = SignalRConstantes.GRP_SMSAGENCE + SignalRConstantes.SEPARATOR + agenceId;
}
else
{
groupName = (SignalRConstantes.GRP_AGENCE + SignalRConstantes.SEPARATOR + agenceId);
}
SignalRMessage signalRMessage = new SignalRMessage(name, "", finalMessage, groupName, theme);
EnvoyerNotificationSms.SendMessageNetSky(signalRMessage);
}
And here is the test code:
[Fact(DisplayName = "Vérifier l'appel à l'infra IEnvoyerNotificationSms")]
public void SendMessageNetSkyToAgenceGrp_CasNormal_ResultatOk()
{
// Arange
var envoyerNotificationSmMock = new Mock<IEnvoyerNotificationSms>();
envoyerNotificationSmMock.Setup(envoyerNotifSms => envoyerNotifSms.SendMessageNetSky(It.IsAny<SignalRMessage>())).Verifiable();
var SignalRCoreService = LocalIocManager.Resolve<ISignalRCoreService>();
// Act
LocalIocManager.IocContainer.UseInstance(envoyerNotificationSmMock.Object, IfAlreadyRegistered.Replace);
SignalRCoreService.SendMessageNetSkyToAgenceGrp(56, "testMessage", "name", true, SignalRMessageThemeEnum.NotificationSms);
// Assert
envoyerNotificationSmMock.Verify(envoyerNotifSms => envoyerNotifSms.SendMessageNetSky(It.IsAny<SignalRMessage>()), Times.Once());
}
But when I execute the test I get an error telling me that the service I want to mock is null (EnvoyerNotificationSms.SendMessageNetSky(signalRMessage);)
The error is : 'Object reference not set to an instance of an object.' in the line EnvoyerNotificationSms.SendMessageNetSky(signalRMessage);
How can I solve this issue?

After checking the problem was the order of these lines :
var SignalRCoreService = LocalIocManager.Resolve<ISignalRCoreService>();
// Act
LocalIocManager.IocContainer.UseInstance(envoyerNotificationSmMock.Object, IfAlreadyRegistered.Replace);
In the second line has no effect, because the service i want to mock is already created in the first line. So the solution was to change the order of the two lines.

Related

Moq why do mocked methods return null and tests till pass?

I am trying to mock the two interfaces below.
Mock<IEmailSender> emailSender = new Mock<IEmailSender>();
Mock<IEmailTemplate> emailTemplate = new Mock<IEmailTemplate>();
Here is the setup
emailTemplate.Setup(x => x.GetForgotPasswordTemplate(It.IsAny<EmailTemplateViewModel>())).Returns(It.IsAny<string>());
emailSender.Setup(x => x.SendEmailAsync(It.IsAny<SendEmailViewModel>(), default)).ReturnsAsync(It.IsAny<SendEmailResultViewModel>());
Here is the controller action that is called.
[EnableCors(PolicyName = "AllowClientAccess")]
[HttpGet("Forgot")]
public async Task<IActionResult> ForgotPassword([FromQuery] string email)
{
var user = await _userManager.FindByEmailAsync(email);
if (user != null)
{
//MOQ file path not found
EmailTemplateViewModel model = new EmailTemplateViewModel();
model.Email = email;
model.RecipientName = user.UserName;
var message = _emailTemplate.GetForgotPasswordTemplate(model);
SendEmailViewModel sendEmailViewModel = new SendEmailViewModel();
sendEmailViewModel.RecipientName = user.UserName;
sendEmailViewModel.RecipientEmail = user.Email;
sendEmailViewModel.Subject = "ForgotPassword";
sendEmailViewModel.Body = message;
await _emailSender.SendEmailAsync(sendEmailViewModel);
return Ok(AddSuccess("Check your email", "Forgot Password"));
}
ModelState.AddModelError("Forgot Password","Unable to send email");
return BadRequest(ModelErrors());
}
This line returns null
var message = _emailTemplate.GetForgotPasswordTemplate(model);
Here is the method code
public string GetForgotPasswordTemplate(EmailTemplateViewModel model)
{
try
{
var utcNow = DateTime.Now;
if (_testEmailTemplate == null)
if (File.Exists("Helpers/Templates/ForgotPasswordEmail.template"))
_testEmailTemplate = ReadPhysicalFile("Helpers/Templates/ForgotPasswordEmail.template");
var appUrl = _configuration.GetSection("ApplicationUrl").Value +
"/reset-password?&email=" + model.Email;
var emailMessage = _testEmailTemplate
.Replace("{user}", model.RecipientName)
.Replace("{testDate}", utcNow.ToString(CultureInfo.InvariantCulture))
.Replace("{appUrl}", appUrl);
return emailMessage;
}
catch (Exception e)
{
Log.Warning(e, "Email error");
throw;
}
}
This line also returns null
await _emailSender.SendEmailAsync(sendEmailViewModel);
Here is the method code
public Task<SendEmailResultViewModel> SendEmailAsync(SendEmailViewModel model, SmtpConfig config = default)
{
model.IsHtml = true;
var from = new MailboxAddress(_config.FromName, _config.FromEmail);
var to = new MailboxAddress(model.RecipientName, model.RecipientEmail);
return SendEmailAsync(#from, new[] {to}, model.Body, model.Body, config, model.IsHtml);
}
Here is the test
[Theory]
[InlineData("stephen#kaizenappz.com")]
public async Task WhenAUserForgetsPasswordAHttpStatusCode200ShouldBeReturnedAsync(string email)
{
var confirmUser = await Controller.ForgotPassword(email);
var result = confirmUser as OkObjectResult;
var actual = (HttpStatusCode)result?.StatusCode.Value;
var expected = HttpStatusCode.OK;
Assert.AreEqual(expected, actual);
}
However the test passes and what i am wondering is why do both of these methods return null and why does the test pass even though it returns null. How do i get these to return something?
One thing I do not understand is when to use It.Any and just pass in a normal object with some test data. How am i supposed to check a user exists if i use It.Any and i need to pass a model into my controller action?
Setup phase
Whenever you need to mock an interface method try be permissive during setup.
In other words allow to receive any parameter:
const string mockedForgotPwdTemplate = "...";
emailTemplate
.Setup(template => template.GetForgotPasswordTemplate(It.IsAny<EmailTemplateViewModel>()))
.Returns(mockedForgotPwdTemplate);
If your return value depends on the parameter
then use that overload of the Returns, which accepts a function:
const string mockedTemplateWithSubject = "...";
const string mockedTemplateWithoutSubject = "...";
emailTemplate
.Setup(template => template.GetForgotPasswordTemplate(It.IsAny<EmailTemplateViewModel>()))
.Returns((EmailTemplateViewModel vm) => !string.IsNullOrEmpty(vm.Subject) ? mockedTemplateWithSubject : mockedTemplateWithoutSubject);
Verification phase
During assertion try to be as specific as possible.
If you have access to the parameter then pass that on to the Verify:
var mockedViewTemplate = new EmailTemplateViewModel { ... };
emailTemplate
.Verify(template => template.GetForgotPasswordTemplate(mockedViewTemplate), Times.Once);
Please bear in mind that moq uses reference check to determine that the expected and actual parameter are the same. If you don't have a reference to this parameter then you should use It.Is<T>:
const string expectedSubject = "ForgotPassword";
emailTemplate
.Verify(template => template.GetForgotPasswordTemplate(
It.Is<EmailTemplateViewModel>(vm => expectedSubject == vm.Subject), Times.Once);
or if you wish to assert on more than one attribute then:
private bool AssertViewModel(EmailTemplateViewModel actualVM, string expectedSubject, string expectedRecipientName)
{
Assert.Equal(expectedSubject, actualVM.Subject);
Assert.Equal(expectedRecipientName, actualVM.RecipientName);
return true;
}
//...
const string expectedSubject = "ForgotPassword", expectedRecipent = "...";
emailTemplate
.Verify(template => template.GetForgotPasswordTemplate(
It.Is<EmailTemplateViewModel>(vm => this.AssertViewModel(vm, expectedSubject, expectedRecipient)), Times.Once);

Unit testing if text == null

I am doing some unit tests with microsoft unit testing
I have this piece of code:
public void AddComment(Case c, string text)
{
if (text == null)
{
return;
}
var comment = UnitOfWork.GetRepository<CaseComment>().Create();
comment.Case = c;
comment.Account = _userInfoProvider.UserName;
comment.DateUtc = DateTimeHelper.UtcNow();
comment.Text = text;
UnitOfWork.GetRepository<CaseComment>().Insert(comment);
}
And I have a Unit test for this piece of cdoe:
if (text == null)
{
return;
}
I the unit test like this:
[TestMethod]
public void BaseProcess_Should_AddCommentIfNull()
{
string text = null;
var result = string.IsNullOrEmpty(text);
Assert.AreEqual(text, null);
}
It shows green. But the code coverage is still yellow and not blue.
Thank you
I have it now like this:
[TestMethod]
public void BaseProcess_Should_AddCommentIfNull()
{
string comment = "Comment";
var newInstance = new Case
{
Reference = comment,
DateSubmitted = DateTime.Now,
Status = CaseStatus.Submitted,
};
string text = null;
var result = string.IsNullOrEmpty(text);
Action act = () => CorrectionRequestCaseProcess.AddComment(newInstance, comment);
Assert.AreEqual(text, null);
}
But if I do it like this:
[TestMethod]
public void BaseProcess_Should_AddCommentIfNull()
{
string comment = "";
var newInstance = new Case
{
Reference = comment,
DateSubmitted = DateTime.Now,
Status = CaseStatus.Submitted,
};
string text = null;
var result = string.IsNullOrEmpty(text);
Action act = () => CorrectionRequestCaseProcess.AddComment(newInstance, text);
Assert.AreEqual(text, null);
}
Nothing changedenter code here
And I wrote a other unit test like this:
[TestMethod]
public void BaseProcess_should_equalToNull()
{
string comment = "Comment";
var newInstance = new Case
{
Reference = comment,
DateSubmitted = DateTime.Now,
Status = CaseStatus.Submitted,
};
var newComment = new CaseComment();
newComment.Case = newInstance;
newComment.Account = _userInfoProvider.UserName;
newComment.DateUtc = DateTimeHelper.UtcNow();
newComment.Text = comment;
var comment2 = _testUnitOfWork.GetRepository<CaseComment>().Create();
_testUnitOfWork.GetRepository<CaseComment>().Insert(newComment);
}
You should add an additional test case that text variable not equal to null and verify that repository invoked. After that, the coverage of the function will be %100.

Is it possible to use a mocked object as an input for another method up for mocking?

I created a previous Test method with setup for two mocked objects on a single Data Access and it worked fine. Did another one with same scenario but this turned out fail.
Here's the test method:
[Test]
public void UpdateUserPassword_WhenInputsAreCorrect_ReturnsQuerySuccessMessage()
{
UpdatePasswordModel input = new UpdatePasswordModel()
{
UserName = "john.doe",
NewPassword = "password1", //password1
PreviousPassword = "password" //password
};
Mock<IUserDataAccess> user = new Mock<IUserDataAccess>();
Mock<IDailyTimeInDataAccess> timeIn = new Mock<IDailyTimeInDataAccess>();
Mock<IDailyTimeOutDataAccess> timeOut = new Mock<IDailyTimeOutDataAccess>();
user.Setup(x => x.UpdatePassword(10000, input.NewPassword)).Returns("User record updated.");
user.Setup(x => x.GetUser(input.UserName)).Returns(new User()
{
UserKey = 10000,
UserName = "john.doe",
UserPassword = "LTg9BIob8urwz643K5+pBA=="
});
ILoginBusinessRules app = new LoginBusinessRules(user.Object, timeIn.Object, timeOut.Object);
var output = app.UpdateUserPassword(input);
Assert.AreEqual("User record updated.", output);
}
Here's the business rule:
public string UpdateUserPassword(UpdatePasswordModel model)
{
if (model == null)
{
return "No data to process.";
}
if (string.IsNullOrEmpty(model.UserName))
{
return "Username is empty.";
}
if (string.IsNullOrEmpty(model.NewPassword))
{
return "New Password is empty.";
}
if (string.IsNullOrEmpty(model.PreviousPassword))
{
return "Previous Password is empty.";
}
var user = _userDataAccess.GetUser(model.UserName);
if (user == null)
{
return "User not found.";
}
if (user.UserPassword != EncryptPassword(model.PreviousPassword))
{
return "Previous password does not match.";
}
else
{
user.UserPassword = EncryptPassword(model.NewPassword);
user.UpdateDttm = DateTime.Now;
user.UpdateUserId = model.UserName;
var result = _userDataAccess.UpdatePassword(user.UserKey, user.UserPassword);
return result;
}
}
The test returned a failure. Further debugging told me that this line here is returning null:
var result = _userDataAccess.UpdatePassword(user.UserKey, user.UserPassword);
Any help greatly appreciated!
The Setup uses input.NewPassword which from the test is
UpdatePasswordModel input = new UpdatePasswordModel() {
//...
NewPassword = "password1",
//...
};
//...
user.Setup(x => x.UpdatePassword(10000, input.NewPassword)).Returns("User record updated.");
//...
but in the method under test the method is called with another value
//...
user.UserPassword = EncryptPassword(model.NewPassword);
//...
var result = _userDataAccess.UpdatePassword(user.UserKey, user.UserPassword);
which wont match what was expected in the setup.
When a mocked member is not invoked with what was expected it will return the default of the return type, which in this case would be null
You would need to either make sure that the correct value is used in the setup expectation
For example
user
.Setup(x => x.UpdatePassword(10000, EncryptPassword(input.NewPassword)))
.Returns("User record updated.");
or loosen the expectation of the setup using an argument matcher like It.IsAny<T>()
user
.Setup(x => x.UpdatePassword(10000, It.IsAny<string>()))
.Returns("User record updated.");
To answer the question in the post, yes you can use any object that matches the input type. Your codes doesn't really know the difference between a "mock" and a "real" object.

Further verify result using MOQ

I have the following code
public bool IsUnitAvailable()
{
this.isUnitAvailable = false;
if(isUnitAvailable == false)
{
var exception = new Exception("Unit Unavailable");
exception.Data.Add("Quotation","1234567");
exception.Data.Add("propertyDate", "2016-10-10"); this.GetElmahExtensionWrapper().LogToElmah(exception);
}
}
return this.isUnitAvailable;
}
and the following unit test.
[TestMethod]
public void WhenUnitIsNotAvailableExceptionShouldBeLoggedInElmahTest()
{
//Arrange
var iPricingServiceMock = new Mock<IPricingService>(MockBehavior.Strict);
iPricingServiceMock.Setup(
counter => counter.IsUnitAvailableOn(It.IsAny<Unit>(),It.IsAny<DateTime>())).Returns(false);
var mockElmahExtensionWrapper = TestHelper.mk.GetMock<IElmahExtensionWrapper>();
// act
var quotation = new Quotation();
quotation.SetElmahExtensionWrapper(mockElmahExtensionWrapper.Object);
quotation.IsUnitAvailable();
//assert
mockElmahExtensionWrapper.Verify(counter => counter.LogToElmah(It.IsAny<Exception>()), Times.Exactly(1));
//change the test to verify that the exception that was logged had 2 Data properties?
}
The unit test is working. How can I change the test to verify that the exception that was logged had 2 Data properties? Changing the code to the following throws a "Cannot resolve symbol Data property" error.
mockElmahExtensionWrapper.Verify
(
counter => counter.LogToElmah
(
It.IsAny<Exception>(ex=>ex.Data.Count == 2)
),
Times.Exactly(1)
);
Change the verification to something like:
mockElmahExtensionWrapper.Verify(counter => counter.LogToElmah(It.Is<TraceException>(ex => ex.Data["Quotation"] == "1234567" && ex.Data["propertyDate"] == "2016-10-10"), Times.Exactly(1));

Moq - Null reference exception when trying to test ActionResult

I'm trying to learn unit testing with Moq but I keep running into the same issue. I am writing unit tests for my controller ActionResults and I don't have a problem with creating an instance of my controller and testing that it works. But I have an ActionResult that gives me a null reference exception because I am missing something with setting up my test before calling the controller.
private PersonController _controller;
private Mock<ICoreRepository> _repoCore;
private Mock<IImageRepository> _repoImage;
[TestInitialize]
public void Setup()
{
_repoCore = new Mock<ICoreRepository>();
_repoImage = new Mock<IImageRepository>();
_controller = new PersonController(_repoImage.Object)
{
CrmCoreRepository = _repoCore.Object,
//ImageRepository = _repoImage.Object
};
}
[TestMethod]
public void UploadImage_NotNull_personImage_Test()
{
var controller = new PersonController(_repoImage.Object);
var model = new GlobalEntityModel
{
PersonModel= new PersonModel()
};
controller.CrmCoreRepository = _repoCore.Object;
var uploadedFile = new Mock<HttpPostedFileBase>();
uploadedFile
.Setup(f => f.ContentLength)
.Returns(10);
uploadedFile
.Setup(f => f.FileName)
.Returns("testimage.jpg");
var imageRepository = new Mock<IImageRepository>();
imageRepository.Setup(x => x.SaveImage(It.IsAny<HttpPostedFileBase>(), It.IsAny<ImagePaths>()));
var res = controller.AddOrUpdatePersonDetail(model, uploadedFile.Object);
Assert.IsNotNull(res);
}
In my test I create a new controller instance, model, a mock of httppostedfilebase and I try to setup the image repository saveImage.
[HttpPost]
public ActionResult AddOrUpdatePersonDetail(GlobalEntityModel model, HttpPostedFileBase personImage)
{
var uploadedImage = string.Empty;
var personImagePath = _imageRepository.RetrieveImageFilePath(ImagePaths.PersonImageFilePath);
if (personImage != null)
{
uploadedImage = _imageRepository.SaveImage(personImage, ImagePaths.PersonImageFilePath);
}
var personModel = model.PersonModel;
var personDto = Mapper.MapTo<PersonDto>(personModel);
//Update Person
personDto.CreatedBy = "Admin";
personDto.UpdatedBy = "Admin";
personDto.UpdatedOn = DateTime.Now;
if (uploadedImage != string.Empty)
{
personDto.PersonImageRef = uploadedImage;
if (personDto.PersonImageRef.Contains(personImagePath))
{
personDto.PersonImageRef = personDto.PersonImageRef.Replace(personImagePath, string.Empty);
}
if (personDto.PersonImageRef.Contains("~/Content"))
{
personDto.PersonImageRef = string.Empty;
}
}
if (personDto.Id == 0)
{
personDto.GlobalEntityGUID = model.GlobalEntityGUID;
personDto = CrmCoreRepository.AddPerson(personDto, null, null, null);
if (personDto != null)
{
personModel.GlobalEntityGUID = personDto.GlobalEntityGUID;
var personDetailModel = model.PersonDetailModel;
personDetailModel.PersonId = personDto.Id;
var personDetailDto = Mapper.MapTo<PersonDetailDto>(personDetailModel);
personDetailDto.CreatedBy = "Admin";
personDetailDto.UpdatedBy = "Admin";
personDetailDto = personDetailModel.Id == 0 ? CrmCoreRepository.AddPersonDetail(personDetailDto) : CrmCoreRepository.UpdatePersonDetail(personDetailDto);
}
}
else if (personDto.Id > 0)
{
personDto = CrmCoreRepository.UpdatePerson(personDto, null, null, null);
if (personDto != null)
{
personModel.GlobalEntityGUID = personDto.GlobalEntityGUID;
var personDetailModel = model.PersonDetailModel;
personDetailModel.PersonId = personDto.Id;
var personDetailDto = Mapper.MapTo<PersonDetailDto>(personDetailModel);
personDetailDto.CreatedBy = "Admin";
personDetailDto.UpdatedBy = "Admin";
personDetailDto = personDetailModel.Id == 0 ? CrmCoreRepository.AddPersonDetail(personDetailDto) : CrmCoreRepository.UpdatePersonDetail(personDetailDto);
}
}
return RedirectToRoute("EditEntity", new { controller = "GlobalEntity", action = "EditEntity", entityGuid = personModel.GlobalEntityGUID, entityType = "P" });
}
personImagePath as shown in the screenshot is null. Because I am not passing a parameter from my test I don't know how to make it contain a value. I am a bit confused with what to do in the unit test to fix this. Any help would be great.
You are using two different instances of IImageRepository. You need to make Setup calls on the same objects that were injected to object you are testing (PersonController - IImageRepository).
To fix it, change
var imageRepository = new Mock<IImageRepository>();
imageRepository.Setup(x => x.SaveImage(It.IsAny<HttpPostedFileBase>(),
It.IsAny<ImagePaths>()));
to (notice Returns)
_repoImage.Setup(x => x.SaveImage(It.IsAny<HttpPostedFileBase>(),
It.IsAny<ImagePaths>())).Returns("~/Content");

Categories

Resources