How to get TempData in an integration test - c#

I have a controller with the following actions:
[HttpGet]
public IActionResult Index()
{
return View();
}
[HttpPost]
[Route(
"/MyShop/OrderDetails/CancelOrder",
Name = UrlRouteDefinitions.MyShopOrderDetailsCancelOrder)]
[ValidateAntiForgeryToken]
public IActionResult CancelOrder(MyViewModel viewModel)
{
var isCancelSuccessful = _orderBusinessLogic.CancelOrderById(viewModel.Order.Id);
if (isCancelSuccessful)
{
//to show a success-message after the redirect
this.TempData["SuccessCancelOrder"] = true;
}
return RedirectToRoute(UrlRouteDefinitions.MyShopOrderDetailsIndex, new
{
orderId = viewModel.Order.Id
});
}
Then I also have the following piece of HTML in the View for the Controller mentioned above:
<div class="panel-body">
#if (TempData["SuccessCancelOrder"] != null)
{
//show the message
#TempData["SuccessCancelOrder"].ToString();
}
</div>
Now I'm writing an integration test (code-summary below) to check if the CancelOrder() method works as expected. There I'd like to access the value of the TempData dictionary to check its correctness.
[TestMethod]
public void MyTest()
{
try
{
//Arrange: create an Order with some Products
var orderId = CreateFakeOrder();
//Open the Order Details page for the arranged Order
var httpRequestMessage = base.PrepareRequest(
HttpMethod.Get,
"/MyShop/OrderDetails?orderId=" + orderId,
MediaTypeEnum.Html);
var httpResponse = base.Proxy.SendAsync(httpRequestMessage).Result;
var responseContent = httpResponse.Content.ReadAsStringAsync().Result;
var viewModel = base.GetModel<MyViewModel>(responseContent);
Assert.AreEqual(HttpStatusCode.OK, httpResponse.StatusCode);
//Try to execute a POST to cancel the Order
httpRequestMessage = base.PrepareRequest(
HttpMethod.Post,
"/MyShop/OrderDetails/CancelOrder",
MediaTypeEnum.Html,
httpResponse, content: viewModel);
httpResponse = base.Proxy.SendAsync(httpRequestMessage).Result;
var expectedRedirectLocation = $"{this.RelativeHomeUrl}/MyShop/OrderDetails?orderId=" + orderId;
var receivedRedirectLocation = WebUtility.UrlDecode(httpResponse.Headers.Location.ToString());
//we expect that the Order Details page will be reloaded
//with the ID of the already cancelled Order
Assert.AreEqual(HttpStatusCode.Redirect, httpResponse.StatusCode);
//-----------------------------------------------------------
//here I'm going to re-execute a GET-request
//on the Order Details page.
//
//Then I need to check the content of the message
//that's been written in the TempData
//-----------------------------------------------------------
}
finally
{
//delete the arranged data
}
}
In any case, I don't know how to access it from my integration test. Does anybody know how to do it and if there's a way at all?

Controller.TempData is public so you can easily access it and check if your key/value exists
[TestMethod]
public void TempData_Should_Contain_Message() {
// Arrange
var httpContext = new DefaultHttpContext();
var tempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>());
var controller = new TestController();
controller.TempData = tempData;
// Act
var result = controller.DoSomething();
//Assert
controller.TempData["Message"]
.Should().NotBeNull()
.And.BeEquivalentTo("Hello World");
}

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);

Sinch CallBack API : Hangup in ICE event

I had place call between two Android devices which is running successfully. Now I want to implement Callback Rest API because of some server side decision before place call. As given in this image, ICE event fire and developer backend respond SVAML response.
Now I want to simply Hangup Call, for this I had done following code :
[System.Web.Http.HttpPost]
public SvamletModel MakeCall(CallbackEventModel model)
{
var sinch = SinchFactory.CreateCallbackResponseFactory(Locale.EnUs);
var reader = sinch.CreateEventReader();
var evt = reader.ReadModel(model);
var builder = sinch.CreateIceSvamletBuilder();
SvamletModel svaml = builder.Hangup().Model;
return svaml;
}
but call still placed. I had also write action class under SvamletModel but same response.
[System.Web.Http.HttpPost]
public SvamletModel MakeCall(CallbackEventModel model)
{
var sinch = SinchFactory.CreateCallbackResponseFactory(Locale.EnUs);
var reader = sinch.CreateEventReader();
var evt = reader.ReadModel(model);
var builder = sinch.CreateIceSvamletBuilder();
SvamletModel svaml = builder.Build().Model;
SvamletActionModel actionModel = new SvamletActionModel();
actionModel.Name = "Hangup";
svaml.Action = actionModel;
return svaml;
}
Note I had followed all steps given in Callback API and https://developers.sinch.com/docs/further-securing-your-sinch-calling-functionality-app-with-rest-api with no success.
Hi your code looks correct, can you capture and send here the HTTP response your backend is sending as response of the ICE POST?
Also add the callID for call.
Sinch Voice & Video Team
Converting return type from SvamletModel to string is working.
So I have changed
[System.Web.Http.HttpPost]
public SvamletModel MakeCall(CallbackEventModel model)
{
var sinch = SinchFactory.CreateCallbackResponseFactory(Locale.EnUs);
var reader = sinch.CreateEventReader();
var evt = reader.ReadModel(model);
var builder = sinch.CreateIceSvamletBuilder();
SvamletModel svaml = builder.Hangup().Model;
return svaml;
}
to
[System.Web.Http.HttpPost]
public string MakeCall(CallbackEventModel model)
{
var sinch = SinchFactory.CreateCallbackResponseFactory(Locale.EnUs);
var reader = sinch.CreateEventReader();
var evt = reader.ReadModel(model);
var builder = sinch.CreateIceSvamletBuilder();
SvamletModel svaml = builder.Hangup().Model;
string json = JsonConvert.SerializeObject(svaml, Formatting.Indented);
return json;
}

c# - Error executing child request for handler

When i re-publish my project to my host, and try to display my view i get this error about 50% of the times depending on how lucky i am
Error executing child request for handler 'System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerAsyncWrapper'.
my view gets called by
#if (Model.Items.Any())
{
var item = Model.Items.FirstOrDefault();
var table = item.GetValue("table");
// Render Action (controllerMethod, ControllerName, UmbracoModelData)
Html.RenderAction(table, "Monitor", new {data = item});
}
and this is my method in the controller that should return my view
public ActionResult Skader(LeBlenderValue data)
{
var jsonString = new WebClient { Encoding = System.Text.Encoding.UTF8 }.DownloadString("myStringHere");
var dbObj = JsonConvert.DeserializeObject<List<SkaderModel>>(jsonString);
var vm = new MonitorTableViewModel(){Skader = dbObj, gridData = data};
return PartialView("~/Views/Partials/Monitor/Skader.cshtml", vm);
}

How to return custom message in web API Post method

I would like the following Post method to return a failure by assigning a failure value to the "result" variable but I am not sure how to achieve that. Ideally it would say that the installation id is invalid, but not sure I could do that:
[Authorize]
[HttpPost, Route("sendForDevelopment")]
public async Task<NotificationOutcome> Post([FromBody]string message, string installationId)
{
string hubName = "myHubName";
string hubNameDefaultShared = "myHubNameDefaultShared";
NotificationHubClient hub = NotificationHubClient
.CreateClientFromConnectionString(hubNameDefaultShared, hubName, enableTestSend: true);
var templateParams = new Dictionary<string, string>
{
["messageParam"] = message
};
NotificationOutcome result = null;
if (string.IsNullOrWhiteSpace(installationId))
{
// output a installation id is null or empty message or assign failure to the result variable
}
else
{
result = await hub.SendTemplateNotificationAsync(templateParams, "$InstallationId:{" + installationId + "}").ConfigureAwait(false);
}
return result;
}
Have the result of the action be a IHttpActionResult derived object.
That should allow greater flexibility as to what you can return when the request is not valid
For example
[Authorize]
[HttpPost, Route("sendForDevelopment")]
public async Task<IHttpActionResult> Post([FromBody]string message, string installationId) {
if (string.IsNullOrWhiteSpace(installationId)) {
var model = new {
error = new {
code = 400,
message = "installation id is null or empty"
}
}
return Content(HttpStatusCode.Badrequest, model); //400 Bad Request with error message
}
string hubName = "myHubName";
string hubNameDefaultShared = "myHubNameDefaultShared";
var hub = NotificationHubClient
.CreateClientFromConnectionString(hubNameDefaultShared, hubName, enableTestSend: true);
var templateParams = new Dictionary<string, string>
{
["messageParam"] = message
};
NotificationOutcome result = await hub.SendTemplateNotificationAsync(templateParams, "$InstallationId:{" + installationId + "}").ConfigureAwait(false);
return Ok(result); //200 OK with model result
}
For a bad request the response body would look something like
{
"error": {
"code": 400,
"message": "installation id is null or empty"
}
}
On the client side you check the status code of the response and proceed accordingly.
var response = await client.PostAsync(url, content);
if(response.IsSuccessStatusCode)
var result = await response.Content.ReadAsAsync<NotificationOutcomeResult>();
//...
else {
//...check why request failed.
var model = await response.Content.ReadAsAsync<ErrorResponse>();
var message = model.error.message;
//...
}
//...

mvc paypal payment details amount dont show

In my project include paypal express check out. I send all details in below class. And My code below;
public class PayPal
{
public static PayPalRedirect ExpressCheckout(PayPalOrder order)
{
var values = new NameValueCollection();
values["USER"] = PayPalSettings.Username;
values["PWD"] = PayPalSettings.Password;
values["SIGNATURE"] = PayPalSettings.Signature;
values["METHOD"] = "SetExpressCheckout";
values["VERSION"] = "63.0";
values["RETURNURL"] = PayPalSettings.ReturnUrl;
values["CANCELURL"] = PayPalSettings.CancelUrl;
values["PAYMENTREQUEST_0_PAYMENTACTION"] = "SALE";
values["PAYMENTREQUEST_0_CURRENCYCODE"] = "USD";
values["PAYMENTREQUEST_0_AMT"] = order.Amount.ToString("0.00", CultureInfo.InvariantCulture);
values["PAYMENTREQUEST_0_DESC"] = "Apart Name";
values = Submit(values);
string ack = values["ACK"].ToLower();
if (ack == "success" || ack == "successwithwarning")
{
return new PayPalRedirect
{
Token = values["TOKEN"],
Url = String.Format("https://{0}/cgi-bin/webscr?cmd=_express-checkout&token={1}",
PayPalSettings.CgiDomain, values["TOKEN"])
};
}
throw new Exception(values["L_LONGMESSAGE0"]);
}
private static NameValueCollection Submit(NameValueCollection values)
{
string data = String.Join("&", values.Cast<string>()
.Select(key => String.Format("{0}={1}", key, HttpUtility.UrlEncode(values[key]))));
var request = (HttpWebRequest)WebRequest.Create(
String.Format("https://{0}/nvp", PayPalSettings.ApiDomain));
request.Method = "POST";
request.ContentLength = data.Length;
using (var writer = new StreamWriter(request.GetRequestStream()))
{
writer.Write(data);
}
using (var reader = new StreamReader(request.GetResponse().GetResponseStream()))
{
return HttpUtility.ParseQueryString(reader.ReadToEnd());
}
}
}
and my controller ;
public ActionResult Pay(FormCollection form)
{
var redirect = PayPal.ExpressCheckout(new PayPalOrder { Amount = 50 });
Session["token"] = redirect.Token;
return new RedirectResult(redirect.Url);
}
But I cant show amount on paypal page????? I show desc but I dont show amount??? what is wrong? thanks for reply.
Have you passing Amount or not, I think you are not passing Amount Value If not then add
public class CartController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Pay()
{
PayPalRedirect redirect = PayPal.ExpressCheckout(new PayPalOrder { Amount = 50 });
Session["token"] = redirect.Token;
return new RedirectResult(redirect.Url);
}
}
For More Details Check PayPal with ASP.NET MVC
Hope it helps you.
Try passing over a line item name and amount and see if it shows up in that case. Also, can you provide the actual string of data you are sending over to PayPal, minus your API credentials so that I can test it with my API credentials.
Example:
https://api-3t.sandbox.paypal.com/nvp?USER=paypal_api1.x.com&PWD=NAEWP67N2BMRSD234P2&SIGNATURE=Ae0iZ4smtdchhBLFKKdS8s8OSA220f033rNWM4EYTk1J-tsdbDOFq0JpNi&METHOD=SetExpressCheckout&VERSION=92.0&RETURNURL=https://www.ccaples.com/mts/pp_nvp_quick_test.php&CANCELURL=https://www.ccaples.com/mts/pp_nvp_quick_test.php&PAYMENTREQUEST_0_PAYMENTACTION=Sale&PAYMENTREQUEST_0_AMT=200&PAYMENTREQUEST_0_ITEMAMT=200&PAYMENTREQUEST_0_SHIPPINGAMT=0.00&PAYMENTREQUEST_0_TAXAMT=0.0&PAYMENTREQUEST_0_CURRENCYCODE=USD&PAYMENTREQUEST_0_DESC=test EC payment

Categories

Resources