Unit Testing using Moq - c#

This is a method under my controller which is used to create dummy keys to encrypt the data in the application and store the same in the amazon s3 bucket.
public JsonResult SaveMasterKeys(string MekText, int Thismek)
{
string folderName = string.Empty, fileName = string.Empty;
List<string> folderNameList = new List<string>();
folderNameList.Add("Guard1");
folderNameList.Add("Guard2");
try
{
if (Thismek == 1)
{
folderName = "Guard1";
fileName = "NewMek1.key";
}
else
{
folderName = "Guard2";
fileName = "NewMek2.key";
}
AWSS3File aws = new AWSS3File();
//aws.BucketExist(filePath);
//aws.CreateFile(MekText, filePath);
// Check Weather the Folder is exist or not
if (!aws.CheckFolderExist(folderName))
{
foreach (var item in folderNameList)
{
aws.CreateFolder(item);
if (item == "Guard1")
{
aws.CreateFileIntoS3((item == folderName ? MekText : ""), item, "NewMek1.key");
aws.CreateFileIntoS3("", item, "Mek1.key");
}
else
{
aws.CreateFileIntoS3((item == folderName ? MekText : ""), item, "NewMek2.key");
aws.CreateFileIntoS3("", item, "Mek2.key");
}
}
}
else
{
aws.CreateFileIntoS3(MekText, folderName, fileName);
}
ViewData["SaveMessage"] = "Saved successfully.";
}
catch (Exception ex)
{
XTP.Logger.LogCritical("XTP.Web.internaltools", ex.ToString());
ViewData["SaveMessage"] = "Keys not updated successfully.";
}
return Json(new { success = true, value = ViewData["SaveMessage"] }, JsonRequestBehavior.AllowGet);
}
And this is the TESTMETHOD I have written for the same
[TestMethod]
public void MockAlways()
{
var mock = new Mock<AccountController>();
JsonResult json = new JsonResult();
//new { success = true, value = ViewData["SaveMessage"] }, JsonRequestBehavior.AllowGet
json.Data = new { success = true, value = "sa" };
json.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
mock.Setup(x => x.SaveMasterKeys("ss", 1)).Returns(json);
var controller = new AccountController();
var result = controller.SaveMasterKeys("ss", 1) as JsonResult;
Assert.AreEqual(mock.Object.SaveMasterKeys("ssxs", 1), result.Data.ToString());
}
I am getting an invalid setup error. Is there a way to resolve this error?

I think that you misunderstood how to mock a controller's action. You are mocking it and then comparing it with the mocked controller. This is not a way to go(it is like checking whether true == true.
Basically this error means that Moq cannot override non-virtual member(it is self-explanatory). You should change method's signature to virtual to allow overriding it.
But - you shouldn't mock action like this. Instead you should mock its dependencies(services, contexts, gateways etc.) and check whether with known input values you can get expected results without mocking the action itself.

Related

How to have an AWS Lambda/Rekognition Function return an array of object keys

This feels like a simple question and I feel like I am overthinking it. I am doing an AWS project that will compare face(s) on an image to a database (s3bucket) of other faces. So far, I have a lambda function for the comparefacerequest, a class library which invokes the function, and an UWP that inputs the image file and outputs a result. It has worked so far being based on boolean (true or false) functions, but now I want it to instead return what face(s) are recognized via an array. I struggling at implementing this.
Below is my lambda function. I have adjusted the task to be an Array instead of a bool and changed the return to be an array. At the bottom, I have created a global variable class with a testing array so I could attempt to reference the array elsewhere.
public class Function
{
//Function
public async Task<Array> FunctionHandler(string input, ILambdaContext context)
{
//number of matched faces
int matched = 0;
//Client setup
var rekognitionclient = new AmazonRekognitionClient();
var s3client = new AmazonS3Client();
//Create list of target images
ListObjectsRequest list = new ListObjectsRequest
{
BucketName = "bucket2"
};
ListObjectsResponse listre = await s3client.ListObjectsAsync(list);
//loop of list
foreach (Amazon.S3.Model.S3Object obj in listre.S3Objects)
{
//face request with input and obj.key images
var comparefacesrequest = new CompareFacesRequest
{
SourceImage = new Image
{
S3Object = new S3Objects
{
Bucket = "bucket1",
Name = input
}
},
TargetImage = new Image
{
S3Object = new S3Objects
{
Bucket = "bucket2",
Name = obj.Key
}
},
};
//compare with confidence of 95 (subject to change) to current target image
var detectresponse = await rekognitionclient.CompareFacesAsync(comparefacesrequest);
detectresponse.FaceMatches.ForEach(match =>
{
ComparedFace face = match.Face;
if (match.Similarity > 95)
{
//if face detected, raise matched
matched++;
for(int i = 0; i < Globaltest.testingarray.Length; i++)
{
if (Globaltest.testingarray[i] == "test")
{
Globaltest.testingarray[i] = obj.Key;
}
}
}
});
}
//Return true or false depending on if it is matched
if (matched > 0)
{
return Globaltest.testingarray;
}
return Globaltest.testingarray;
}
}
public static class Globaltest
{
public static string[] testingarray = { "test", "test", "test" };
}
Next, is my invoke request in my class library. It has so far been based on the lambda outputting a boolean result, but I thought, "hey, it is parsing the result, it should be fine, right"? I do convert the result to a string, as there is no GetArray, from what I know.
public async Task<bool> IsFace(string filePath, string fileName)
{
await UploadS3(filePath, fileName);
AmazonLambdaClient client = new AmazonLambdaClient(accessKey, secretKey, Amazon.RegionEndpoint.USWest2);
InvokeRequest ir = new InvokeRequest();
ir.InvocationType = InvocationType.RequestResponse;
ir.FunctionName = "ImageTesting";
ir.Payload = "\"" + fileName + "\"";
var result = await client.InvokeAsync(ir);
var strResponse = Encoding.ASCII.GetString(result.Payload.ToArray());
if (bool.TryParse(strResponse, out bool result2))
{
return result2;
}
return false;
}
Finally, here is the section of my UWP where I perform the function. I am referencing the lambda client via "using Lambdaclienttest" (name of lamda project, and this is its only instance I use the reference though). When I run my project, I do still get a face detected when it should, but the Globaltest.testingarray[0] is still equal to "test".
var Facedetector = new FaceDetector(Credentials.accesskey, Credentials.secretkey);
try
{
var result = await Facedetector.IsFace(filepath, filename);
if (result)
{
textBox1.Text = "There is a face detected";
textBox2.Text = Globaltest.testingarray[0];
}
else
{
textBox1.Text = "Try Again";
}
}
catch
{
textBox1.Text = "Please use a photo";
}
Does anyone have any suggestions?

How to properly setup mock data for unit test?

Having some issues setting up my mock data for a unit test in my API. The data is retrieved from a service.
Here is my endpoint and how the data is retrieved:
RetrieveExceptionReportSessionDatesResponse response
= await ResolveServiceClient().RetrieveExceptionReportSessionDatesAsync(new RetrieveExceptionReportSessionDatesRequest());
List<ExceptionReportSessionDataModel> result
= GetSessionData(response.RetrieveExceptionReportSessionDatesResult);
if (result != null && result.Count > 0)
{
logText = LogFormatter.Format(
WebUtilities.GetUser((ClaimsIdentity)HttpContext.User.Identity),
startTime, DateTime.Now, Privilege.EditSession,
"Get Exception Report Session Data", "Exception Report Session Data retrieved successfully.");
logger.LogInfo(logText);
}
else
{
logText = LogFormatter.Format(
WebUtilities.GetUser((ClaimsIdentity)HttpContext.User.Identity),
startTime, DateTime.Now, Privilege.ViewOrderExceptionReport,
"Get exception report session data", "Exception report session data is null or empty.");
logger.LogWarn(logText);
}
return Ok(result);
Here is what I have so far in setting up my unit test:
//Arrange
List<ExceptionReportSessionDataModel> sessionData = new List<ExceptionReportSessionDataModel>()
{
new ExceptionReportSessionDataModel() {SessionName = "Session1", ReportFiles = null },
new ExceptionReportSessionDataModel() {SessionName = "Session2", ReportFiles = null },
new ExceptionReportSessionDataModel() {SessionName = "Session3", ReportFiles = null }
};
//NEED HELP HERE
var ERSessionDataMock = new Mock<>();
ERSessionDataMock.Setup(x => x.).Returns();
var loggerMock = new Mock<ILogger>();
loggerMock.Setup(x => x.LogInfo(null));
GetSessionData Method:
public List<ExceptionReportSessionDataModel> GetSessionData(string sessionData)
{
List<ExceptionReportSessionDataModel> reports = new List<ExceptionReportSessionDataModel>();
if (!string.IsNullOrWhiteSpace(sessionData))
{
string[] splitString = sessionData.Split("\n", StringSplitOptions.RemoveEmptyEntries);
foreach (string s in splitString)
{
string[] temp = s.Split(",", StringSplitOptions.RemoveEmptyEntries);
List<string> files = new List<string>();
for (int index = 1; index < temp.Length; index++)
{
files.Add(temp[index]);
}
reports.Add(new ExceptionReportSessionDataModel()
{
ReportFiles = files,
SessionName = temp[0]
});
}
}
return reports;
}
I need help setting up the ERSessionDataMock
It is impossible to know without seeing how the GetSessionData method is implemented. But here are some quick suggestions:
Inject your ServiceClient as a dependency instead of calling a method to get it. It needs to either:
be an interface (like IServiceClient) - then in your Startup.cs class:
services.AddScoped<IServiceClient>(sp => ResolveServiceClient().Result);
or the RetrieveExceptionReportSessionDatesAsync method needs to be virtual or abstract so you can override it with a mock:
public virtual RetrieveExceptionReportSessionDatesAsync(RetrieveExceptionReportSessionDatesRequest request);
Then in your test, create a response variable that will exercise the GetSessionData in a certain way to get either an empty List<ExceptionReportSessionDataModel> or not, depending on the test (again, no way to know exactly how without seeing your logic):
var response = new RetrieveExceptionReportSessionDatesResponse();
And override the RetrieveExceptionReportSessionDatesAsync method on the mock:
var serviceClient = new Mock<IServiceClient>();
serviceClient
.Setup(x => x.RetrieveExceptionReportSessionDatesAsync(It.IsAny<RetrieveExceptionReportSessionDatesRequest>()))
.Returns(response);
Now, if you've setup response in a way that triggers GetSessionData to return a List<ExceptionReportSessionDataModel> that has Count > 0, you can verify that log message, otherwise verify the opposite log message.
But IMO, the way you've written this code makes unit testing a chore. You don't seem to be taking advantage of dependency injection.
UPDATE:
It seems you can either assign response as:
var response = new RetrieveExceptionReportSessionDatesResponse
{
RetrieveExceptionReportSessionDatesResult = Guid.NewGuid().ToString()
}
and verify that logger.LogInfo is called with Privilege.EditSession, or assign response as:
var response = new RetrieveExceptionReportSessionDatesResponse
{
RetrieveExceptionReportSessionDatesResult = string.Empty
}
and verify that logger.LogWarn is called with Privilege.ViewOrderExceptionReport.
Just one more observation - is there an off-by-one error in the for loop? (It is staring at index 1)

Download a file in FORM POST request C#

I am using C# ASP.NET code and trying to download file on the post request of a form. here is my sample code.
[HttpPost]
public ActionResult PostMethodName(PostModel inputModel)
{
if (ModelState.IsValid)
{
//other code is removed.
//Writing this for the test
//Download Method call
DownloadCertificate("This is the test file to download.");
var statusHtml = RenderViewToString("Status",
new ErrorMsgModel
{
IsSuccess = true,
ErrorDesc = "desc"
});
return Json(new { IsSuccess = true, ErrorDescription =
statusHtml}, JsonRequestBehavior.AllowGet);
}
var statusHtml1 = RenderViewToString("Status",
new ErrorMsgModel
{
IsSuccess = false,
ErrorDesc = "desc"
});
statusHtml1 = statusHtml1.Replace("'", "\\'");
statusHtml1 = statusHtml1.Replace(Environment.NewLine, "");
return Json(new { IsSuccess = false, ErrorDescription = statusHtml1
}, JsonRequestBehavior.AllowGet);
}
Download method which is called from this method.
public ActionResult DownloadCertificate(string content)
{
//Certificate Download
const string fileType = "application/pkcs10";
string fileName = "Certificate" + DateTime.Today.ToString(#"yyyy-MM-dd") + ".csr";
var fileContent = String.IsNullOrEmpty(contrnt) ? "" : contrnt;
byte[] fileContents = Encoding.UTF8.GetBytes(fileContent);
var result = new FileContentResult(fileContents, fileType) { FileDownloadName = fileName };
return result;
}
file download is not working, post functionality is working as desired.
[HttpPost]
public ActionResult DownloadCertificate(PostModel inputModel, string content)
{
if(!ModelState.IsValid){return Json(new {Success=false,//error descr})}
//Certificate Download
const string fileType = "application/pkcs10";
string fileName = "Certificate" + DateTime.Today.ToString(#"yyyy-MM-dd") + ".csr";
var fileContent = String.IsNullOrEmpty(contrnt) ? "" : contrnt;
byte[] fileContents = Encoding.UTF8.GetBytes(fileContent);
var result = new FileContentResult(fileContents, fileType) { FileDownloadName = fileName };
return result;
}
In your previous code you don`t use DownloadCertificate result, you simly execute it.
Your DownloadCertificate method returns a value, but you never use the return value in your PostMethodName method.
Given that you are returning json from that method I would suggest that you return a direct link to the file result in the response. The consuming client can then initiate the download. Something like:
return Json(new { IsSuccess = true, Location = Url.Action("DownloadContent")});
Alternatively you could consider a more restful approach and return a 302 response from the post action:
if (ModelState.IsValid)
{
// you code here
return RedirectToAction("Controller", "DownloadContent", new {content = "myContent"});
}
This may well proceed with the download transparently depending on your client whilst keeping to the Post-Redirect-Get pattern

c# unit test fails with moq when it goes to hit a real service

So I was put on doing unit test and i noticed different unit tests failing in nunit and in Visual Studio with Resharper i tried debugging it and I get object
[Test]
public void KeyDocumentService_ProofKeyDocument_RepoReturnsData_ServiceReturnsTheDataWithoutError()
{
//Arrange
KeyDocumentProofRequest request = new KeyDocumentProofRequest() { KeyDocumentId = 2 };
string returnedResponse = "2";
KeyDocument keyDocumentResponse = new KeyDocument() { CampaignId = "2", DesignFileId = 3,DocumentId="2", DataSourceId="3", KeyDocumentId=1 };
List<vwKeyDocumentSearch> keyListResponse = new List<vwKeyDocumentSearch>() { new vwKeyDocumentSearch { FieldName = "test", FieldValue = "testvalue" } };
var uproduceRepo = new Mock<IUProduceRepository>();
var keyDocRepo = new Mock<IKeyDocumentRepository>();
var templateRepo = new Mock<ITemplateRepository>();
keyDocRepo.Setup(p => p.GetKeyDocument(It.IsAny<KeyDocumentRequest>())).Returns(new KeyDocumentResponse() { data = keyDocumentResponse });
keyDocRepo.Setup(p => p.GetKeyDocumentItems(It.IsAny<int>())).Returns(keyListResponse);
uproduceRepo.Setup(p => p.ProduceDocument(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Customization[]>(), It.IsAny<string>(), It.IsAny<string>(), null)).Returns(returnedResponse);
// Act.
KeyDocumentService svc = new KeyDocumentService(keyDocRepo.Object, uproduceRepo.Object, templateRepo.Object);
var response = svc.ProofKeyDocument(request);
//Assert
Assert.IsNotNull(response);
Assert.IsNotNull(response.data.JobId);
Assert.IsNull(response.Error);
}
So the error is happening on this line :
var response = svc.ProofKeyDocument(request);
Are unit Tests supposed to even be going into a real service? or is that ok?
That Method ProofKeyDocument looks like this FYI
private List<Customization> GetCustomizationsFromKeyDocumentItems(List<vwKeyDocumentSearch> keyDocumentItemsList,
int templateId, int clientId)
{
try
{
List<Customization> KeyDocumentCustomizations = new List<Customization>();
var keyDocumentVariableList = keyDocumentItemsList.Where(k => k.Type.ToUpper()=="VARIABLE").ToList();
var keyDocumentSettingList = keyDocumentItemsList.Where(k => k.Type.ToUpper() == "SETTING").ToList();
var keyDocumentContentList = keyDocumentItemsList.Where(k => k.Type.ToUpper() == "CONTENT").ToList();
KeyDocumentCustomizations.AddRange(VariableCustomizations(keyDocumentVariableList, templateId));
KeyDocumentCustomizations.AddRange(SettingCustomizations(keyDocumentSettingList, templateId));
KeyDocumentCustomizations.AddRange(ContentCustomizations(keyDocumentContentList, templateId, clientId));
return KeyDocumentCustomizations;
}
catch (Exception ex)
{
logger.Error(string.Format("Error customizing key document: {0}", templateId), ex);
throw ex;
}
}
I see with Debugging it blowing up on this line
var keyDocumentVariableList = keyDocumentItemsList.Where(k => k.Type.ToUpper()=="VARIABLE").ToList();
Object Reference not set to instance... error Why ?
var keyDocumentResponse = _repo.GetKeyDocument(new KeyDocumentRequest() { KeyDocumentId = request.KeyDocumentId });
and
Customization[] customizations = GenerateCustomizationsForKeyDocument(keyDocumentDetails.KeyDocumentId, keyDocumentResponse);
Then
public KeyDocumentProofResponse ProofKeyDocument(KeyDocumentProofRequest request)
{
//return new KeyDocumentProofResponse()
//{
// data = new KeyDocumentProofResponseData() { JobId = "2984" }
//};
KeyDocumentProofResponse response = new KeyDocumentProofResponse();
var keyDocumentDetails = _repo.GetKeyDocument(new KeyDocumentRequest() { KeyDocumentId = request.KeyDocumentId }).data;
if (keyDocumentDetails != null && (!string.IsNullOrEmpty(keyDocumentDetails.CampaignId)) &&
keyDocumentDetails.DesignFileId.HasValue &&
keyDocumentDetails.DesignFileId > 0)
{
var keyDocumentResponse = _repo.GetKeyDocument(new KeyDocumentRequest() { KeyDocumentId = request.KeyDocumentId });
Customization[] customizations = GenerateCustomizationsForKeyDocument(keyDocumentDetails.KeyDocumentId, keyDocumentResponse);
var jobTicketId = _uproduceRepo.CreateJobTicket(keyDocumentDetails.DocumentId, keyDocumentDetails.DataSourceId, "PROOF");
if (!string.IsNullOrEmpty(jobTicketId))
{
List<JobDataSource> dataSources = GenerateCSVForPersonalizedAndCustomizedVariables(keyDocumentResponse, jobTicketId);
var jobId = _uproduceRepo.ProduceDocument(keyDocumentDetails.DocumentId, keyDocumentDetails.DataSourceId, customizations, "PROOF", jobTicketId, dataSources);
if (string.IsNullOrEmpty(jobId))
{
response.Error = CreateCustomError("Error while submitting job", "Error occurred while submitting proofing job");
}
else
{
response.data.JobId = jobId;
}
}
else
{
response.Error = CreateCustomError("Unable to generate job ticket for the keydocument",
"Error while creating a job ticket for proof request");
}
}
else
{
response.Error = CreateCustomError("Unable to generate proof for the keydocument",
"Requested template is missing campaignid or Designfile in Uproduce");
}
return response;
}
Published test doesn't contain mock method for "CreteJobTicket" and jobTickedId will be null.
var jobTicketId = _uproduceRepo.CreateJobTicket(keyDocumentDetails.DocumentId, keyDocumentDetails.DataSourceId, "PROOF");

Using Moq to unit test but the object returns Null

I'm new to using Moq. I am trying to get this unit test to work but my object seems to keep returning null. I saw online that the Setup() must match the actual call. I'm obviously not getting it cause it still doesn't work; it seems to match to me. Here's my code samples.
Test method from test project:
[TestMethod]
public void CanPutEmailOptOut()
{
var mockParticipant = new PscuParticipant
{
ParticipantId = 1,
DoNotSendCuRewardsEmails = false,
DoNotSendEarnBonusPointEmail = false,
CardNumber = "VPZS5zXFUex2SJikkXFVrnvt2/R38yomFXwkslgXNKkgAFsjvt94p1h6J/XUEc6yQ5JzmT6+W8AdxuBSbp9e0SXAN60oHuZtWhAgGHhU+GaxJfCQHitc2+VBSZ/DxwW7Bpw="
};
MockBootstrapper.Instance.WithRepositoryData(new[] {mockParticipant});
var input = new EmailOptOutContract
{
DoNotSendCuRewardsEmails = true,
DoNotSendEarnBonusPointEmail = true
};
_affinityOptOutApiClient
.Setup(
x =>
x.CallAffinityOptOutStatus(It.IsAny<string>(),
It.IsAny<string>(),
mockParticipant.DoNotSendEarnBonusPointEmail,
mockParticipant.ParticipantId))
.Returns<HindaHttpResponse<OptOutResponse>>(x => new HindaHttpResponse<OptOutResponse>
{
StatusCode = AffinityResultCode.Success,
ResponseObject = new OptOutResponse { MemberId = "999999999", Status = "success" }
});
var response = Controller.Put(mockParticipant.ParticipantId, input);
var contract = response.ShouldBeSuccess<SuccessContract>();
var participant = RepositoryFactory.CreateReadOnly<PscuParticipant>().FirstOrDefault(x => x.ParticipantId == mockParticipant.ParticipantId);
Assert.AreEqual(input.DoNotSendCuRewardsEmails, participant.DoNotSendCuRewardsEmails);
Assert.AreEqual(input.DoNotSendEarnBonusPointEmail, participant.DoNotSendEarnBonusPointEmail);
}
protected override void Configure()
{
MockBootstrapper.Override(config => config.For<IEncryptionService>().Use<EncryptionService>());
_affinityOptOutApiClient = new Mock<IAffinityOptOutApiClient>(MockBehavior.Strict);
MockBootstrapper.Override(config => config.For<IAffinityOptOutApiClient>().Use(_affinityOptOutApiClient.Object));
}
Here's the method from my controller:
public HttpResponseMessage Put(int participantId, [FromBody]EmailOptOutContract contract)
{
if (contract == null)
return Failure(ApiReturnCodes.InvalidRequestContract
, "Invalid Request Contract",
string.Format("Contract Is Null in controller method {0}", System.Reflection.MethodBase.GetCurrentMethod()),
HttpStatusCode.BadRequest);
using (new UnitOfWorkScope())
{
var participant = GetParticipant(participantId);
if (participant == null)
{
return NotFound(ApiReturnCodes.ParticipantNotFound, "Participant ID not found.");
}
participant.DoNotSendCuRewardsEmails = contract.DoNotSendCuRewardsEmails;
participant.DoNotSendEarnBonusPointEmail = contract.DoNotSendEarnBonusPointEmail;
string cardNumber = ServiceLocator.Current.GetInstance<IEncryptionService>().Decrypt(participant.CardNumber);
cardNumber = AesEncrypt(cardNumber);
string email = null;
var partic = GetParticipantData(participant.ParticipantId);
if (partic != null)
email = partic.Email;
HindaHttpResponse<OptOutResponse> response =
_affinityOptOutApiClient.CallAffinityOptOutStatus(cardNumber, email, contract.DoNotSendEarnBonusPointEmail, participant.ParticipantId);
if (response.StatusCode == AffinityResultCode.Success && response.ResponseObject.Status == "success")
participant.AffinityMembId = response.ResponseObject.MemberId;
else
return BadRequest(ApiReturnCodes.AffinityInternalServerError, response.ExternalErrorMessage);
return Ok();
}
}
The part that comes back null in the controller is
HindaHttpResponse<OptOutResponse> response =
_affinityOptOutApiClient.CallAffinityOptOutStatus(cardNumber, email, contract.DoNotSendEarnBonusPointEmail, participant.ParticipantId);
The response object is null so when it is checked in the next statement for success, the exception is thrown. Does anyone know what might be wrong with my Setup/Return that's causing problems?
Thanks!!!!
In your controller you're changing participant.DoNotSendCuRewardsEmails to the value in the contract object, which in your setup of that is false. You setup your method to expect true for that parameter as that is the value contained in participant when setup is called. Moq gets the value of the property as is when setup is called, it doesn't lazy evaluate the objects property.
When you setup the mock, you have to use input
x.CallAffinityOptOutStatus(
It.IsAny<string>(),
It.IsAny<string>(),
input.DoNotSendEarnBonusPointEmail,
mockParticipant.ParticipantId)
It needs to match the specific call you are making inside the controller.

Categories