Nunit tests with MvcApprovals Verify specific url - asp.net-mvc

the project url of my mvc application is hosted like this localhost:port/AppName
I created a new unit test project to test the mvc app using nunit framework and for approval tests I used Approval Tests Library
[Test]
[UseReporter(typeof(FileLauncherReporter),typeof(ClipboardReporter))]
public void PaymentPopUpController_IndexView()
{
//Arrange
PortFactory.MvcPort = 8080;
// Act
Func<ActionResult> ScopeControllerTest = new ScopeController()._Index;
// Approve
MvcApprovals.VerifyMvcPage(ScopeControllerTest);
}
MvcApprovals.VerifyMvcPage throws an error saying that it couldn't find the url since I didn't set the right project url :
The Following error occurred while connecting to:
http: // localhost: 8080/ Scope / _index
error:
The remote server returned an error: (404) Not Found.
So my question is how to set the right url to set with nunit for mvc approval test

MvcApprovals will construct the url from the combination of the Controller name + the Method name.
So in this case the new ScopeController()._Index is being translated to
Scope/_Index
There are 2 possibilities as to why this isn't working
1) Bad url. Possibly the _index isn't public or otherwise isn't the correct route. The easiest way to test this is simply to open a web browser and point it at
http://localhost:8080/Scope/_index
If your browser can't see this url then neither can approvaltests. (unless you are using cassinidev)
2) The second reason is you might not have the web server turned on during the test. This would also be shown by opening the url in a browser.
Happy Testing!
Llewellyn Falco

Related

Async Function Fails when called as part of a Constructor

I'm rather new to Blazor, but I am currently trying to get access to some classes from within a class library that I've created and deployed as a Nuget package. As background, the Nuget package is an Api library, which allows me to talk to a webservice (I don't know if this is relevant or not). However, every time I go to the page where I'm testing, the page never loads and instead I left looking at the browser loading circle until I navigate away or close the application. During my testing here, it seems like it's the #inject call of my interface into the Blazor component which is causing the issue as when I remove it and try to load the page normally, the page does so.
So to demonstrate what I have setup, here is where I've added the Singletons to the DI:
builder.Services.AddSingleton<IApiConfigHelper, ApiConfigHelper>();
builder.Services.AddSingleton<IApiHelper, ApiHelper>();
builder.Services.AddSingleton<ISystemEndpoint, SystemEndpoint>();
Then on the blazor page, I have the following declarations at the top of my page:
#using Library.Endpoints
#using Library.Models
#page "/"
#inject ISystemEndpoint _systemEndpoint
Now I am leaning towards is this something to do with the Nuget package and using it with DI. I have tested the library away from this project (In a console application) and can confirm it's working as it should.
I have also created a local class library as a test to, to see if I could inject a data access class into the page and I can confirm that this works without an issue, which suggests to me that DI is working, just not with my Nuget package.
I did have a look into CORS, given that the Nuget package is accessing an external domain, and setup the following simple CORS policy in the app:
builder.Services.AddCors(policy =>
{
policy.AddPolicy("OpenCorsPolicy", opt =>
opt.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
});
Which is added to the app after the AddRouting call like so:
app.UseCors("OpenCorsPolicy");
However again, this wasn't the solution so if anyone is able to point me in the right direction with where I may be going wrong with this or offer any advice, I would be most grateful.
EDIT 1 - Provides details #mason queried
Regarding SystemEndpoint, the constructor is being injected with 2 things, as below:
public SystemEndpoint(IApiHelper apiHelper, IOptions<UriConfigModel> uriOptions)
{
_apiHelper = apiHelper;
_uriOptions = uriOptions.Value;
}
My Nuget Library is dependant on the following:
Azure.Identity
Azure.Security.KeyVault.Secrets
Microsoft.AspNet.WebApi.Client
Microsoft.Extensisons.Options.ConfigurationExtensions
EDIT 2 - Doing some further testing with this I have added a simple Endpoint class to my Nuget library, which returns a string with a basic message, as well as returning the values of the 2 UriConfig properties as below. I added this test to 1) sanity check that my DI was working correctly, and 2) check the values that are being assigned from appsettings to my UriConfig Object.
public class TestEndpoint : ITestEndpoint
{
private readonly IOptions<UriConfigModel> _uriConfig;
public TestEndpoint(IOptions<UriConfigModel> uriConfig)
{
_uriConfig = uriConfig;
}
public string TestMethod()
{
return $"You have successfully called the test method\n\n{_uriConfig.Value.Release} / {_uriConfig.Value.Version}";
}
}
However when adding in the dependency of IApiHelper into the Ctor, the method then breaks and fails to load the page. Looking into ApiHeloer, the Ctor has a dependency being injected into it of IApiConfigHelper. Looking at the implementation, the Ctor of ApiConfigHelper is setting up the values and parameters of the HttpClient that should make the REST calls to the external Api.
Now I believe what is breaking the code at this point is a call I'm making to Azure Key Vault, via REST, to pull out the secret values to connect to the Api. The call to KeyVault is being orchestrated via the following method, making use of the Azure.Security.KeyVault.Secrets Nuget Package, however I assume that at the heart of it, it's making a REST call to Azure on my behalf:
private async Task<KeyVaultSecret> GetKeyVaultValue(string secretName = "")
{
try
{
if (_secretClient is not null)
{
var result = await _secretClient.GetSecretAsync(secretName);
return result.Value;
}
}
catch (ArgumentException ae)
{
Console.WriteLine(ae.Message);
}
catch (Azure.RequestFailedException rfe)
{
Console.WriteLine(rfe.Message);
}
return new(secretName, "");
}
So that's where I stand with this at the moment. I still believe it could be down to CORS, as it seems to be falling over when making a call to an external service / domain, but I still can say 100%. As a closing thought, could it be something as simple as when I call call the above method, it's not being awaited????
So after persisting with this it seems like the reason it was failing was down to "awaiting" the call to Azure KeyVault, which was happening indirectly via the constructor of ApiConfigHelper. The resulting method for getting KeyVault value is now:
private KeyVaultSecret GetKeyVaultValue(string secretName = "")
{
try
{
if (_secretClient is not null)
{
var result = _secretClient.GetSecret(secretName);
if (result is not null)
{
return result.Value;
}
}
}
catch (ArgumentException ae)
{
Console.WriteLine(ae.Message);
}
catch (Azure.RequestFailedException rfe)
{
Console.WriteLine(rfe.Message);
}
return new(secretName, "");
}
I am now able to successfully make calls to my library and return values from the Api it interacts with.
I can also confirm that this IS NOT a CORS issue. Once I saw that removing the await was working, I then removed the CORS policy declarations from the service and the app in my Blazor's start-up code and everything continued to work without an issue.
As a final note, I must stress that this is only seems an issue when using the library with Blazor (possibly webApi projects) as I am able to use the library, awaiting the Azure call just fine in a console application.

SpecsFor MVC - Can't get site to build / appear

I am attempting to create some UI tests using SpecsFor MVC, I am coming at this from a new user's point of view in terms of testing so could be easily missing something obvious.
The site I'm testing against already uses it's own test DB so I do not need to create one. when I build and debug normally on this site it also starts up a couple of WCF projects which we use for service layer interactions. Presumably I'll need to start these in the specs config but have not got that far yet.
I've followed the documentation and have created this method:
protected override void AfterConfigurationApplied()
{
var config = new SpecsForMvcConfig();
config.UseIISExpress()
.With(Project.Named("TestSite"))
.UsePort(55555)
.CleanupPublishedFiles()
.UseMSBuildExecutableAt(#"C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe");
config.BuildRoutesUsing(MvcApplication.RegisterRoutes);
config.UseBrowser(BrowserDriver.Chrome);
_host = new SpecsForIntegrationHost(config);
_host.Start();
}
The routing of the site is set in the Global.asax hence the setting in the above method.
I also have this very basic test in place to just see if I can get it working:
protected override void When()
{
SUT.NavigateTo<HomeController>(u => u.Index());
SUT.FindLinkTo<HomeController>(u => u.About())
.Click();
}
When I debug the tests it successfully starts Chrome but hangs for ages and eventually fails with this error:
OpenQA.Selenium.WebDriverException: The HTTP request to the remote WebDriver server for URL http://localhost:49924/session/bd15d6a15395b4ca204437c340639501/element timed out after 60 seconds.
I'm not sure where that port number or session etc are coming from.
If I'm running my web project normally (outside of this whole testing project) I see a URL like this:
https://localhost:55555
I would have thought I'd see something similar for these tests cases? It doesn't really matter in any case because they're not working.
Am I missing some element of the config? Do I need to set up the WCF layer to run as well?

I Tried do a simple unit testing in mvc 2 but it throws this error .Can any one Help me

I have to do a unit test for one method or function in MVC 2 .It will work on windows application ,But it shows the following Error Message in mvc2 can any one help me please
The Web request 'LocalHost' completed successfully without running the test. This can occur when configuring the Web application for testing fails (an ASP.NET server error occurs when processing the request), or when no ASP.NET page is executed (the URL may point to an HTML page, a Web service, or a directory listing). Running tests in ASP.NET requires the URL to resolve to an ASP.NET page and for the page to execute properly up to the Load event. The response from the request is stored in the file 'WebRequestResponse_HelloTest.html' with the test results; typically this file can be opened with a Web browser to view its contents.
here is my code Below
Home Controller:
public string Hello()
{
return "This is my First Unit Testing";
}
After that Right Click the controller , select the specified method and done a unit testing.
Here the testing Code below
HomeControllerTest.cs
[TestMethod()]
[HostType("ASP.NET")]
[AspNetDevelopmentServerHost("C:\\Users\\user\\Desktop\\praveen\\adcd\\adcd", "/")]
[UrlToTest("http://localhost:50332/")]
public void HelloTest()
{
HomeController target = new HomeController(); // TODO: Initialize to an appropriate value
string expected = "This IS my First Unit Testing"; // TODO: Initialize to an appropriate value
string actual;
actual = target.Hello();
Assert.AreEqual(expected, actual);
// Assert.Inconclusive("Verify the correctness of this test method.");
}
TestFiles:
WebRequestResponse_HelloTest.html
Finally I got a answer once i removed a [HostType("ASP.NET")] it will check my string and show the success message.Dont know how it works nu.Thanks for your effort to analyse my bug.
The controller is simply a class. You don't need to have the asp.net development server running in order to test your output. It depends on what you're trying to accomplish with your test but for me I'll write unit tests to verify my logic in the actions. If you want functional tests where your hitting your application and your having the test interact with your app via a browser driver and code as it exists on your local IIS or asp.net development server then my example won't help. If you want to just test your code/logic in the control then here's an example of a unit test to test the output of your controller.
[TestClass]
public class HomeControllerTests
{
[TestMethod]
public void Hello_ReturnsString()
{
// Arrange
const string expectedOutput = "This IS my First Unit Testing";
HomeController controller = new HomeController();
// Act
string actualResult = controller.Hello();
// Assert
Assert.AreEqual(expectedOutput, actualResult, "Expected the result to be 'This IS my First Unit Testing'");
}
}

breeze 1.3.6 cannot get metadata

The Devcurry hottowelsignalr example which utilizes breeze works fine until i upgraded breeze via nuget to 1.3.6.
F12 in chrome shows that it is trying to retrieve metadata from http://localhost/api/breeze/Metadata whereas previously it would use
http://localhost/OnlineCollaborationWithSignalR/api/breeze/Metadata. Which is the correct location of the call. It appears that the path of the application is missing from the root. i.e. (OnlineCollaborationWithSignalR)
Update
noticed the release notes for 1.3.1. And subsequently changed the routing to
public static class BreezeWebApiConfig {
public static void RegisterBreezePreStart() {
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "BreezeApi",
routeTemplate: "breeze/{controller}/{action}"
);
}
}
and the app/viewmodels/home.js to
// service name is route to the Web API controller
var serviceName = 'breeze/Breeze';
from
// service name is route to the Web API controller
var serviceName = 'api/Breeze';
It still fails with the same error as noted above.
Can you confirm that the API responds accordingly?
Based on your routeTemplate, it should be.
http://localhost/breeze/Breeze/Metadata
UPDATE:
Yes, if your configuration is such. It should be
http://localhost/OnlineCollaborationWithSignalR/breeze/Breeze/Metadata
I am not familiar with the specific project, but I downloaded and updated it. I then made the changes that you specified, and I am getting a response from the API. Although I am getting an error (most likely unrelated), it is in fact routed properly.
For the record, the error I get is:
"The provider for invariant name 'System.Data.SqlClient' is specified multiple times in the application configuration. The invariant name must be unique for each configured provider."

ASP.net MVC SPA routing

I'm planning to build a SPA with asp.net MVC4 but I'm not quite sure how I have to Setup my Project because of the Routing. Most SPA's work with hashrouting like this mypage/#/News/today for instance.
What would happen if the browses directly to mypage/News/today if I haven't specified a Controller named News with an action today?
The App should handle both types of Routing, how can I achieve this?
Do I have to build my App in a classic way, like Adding several Controllers with appropriate Actions and views and also build a clientside MVC structure with knockout, jquery etc?
You'll have to let all routes to "pages" fall through to let your SPA handle them (including essentially fake 404s if it's not to a real page in your SPA), but at the same time, need to make sure that you get the correct responses for API calls and/or file requests.
Below is the setup I have (I am using Vue as the js framework but that doesn't matter much for this, and not at all for the server-side piece).
First, add this to your Startup.cs, in addition to your default route setup:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.Use(async (context, next) =>
{
await next();
var path = context.Request.Path.Value;
// If there's no available file and the request doesn't contain an extension, we're probably trying to access a page
if (context.Response.StatusCode == (int)HttpStatusCode.NotFound && !Path.HasExtension(path) && !path.StartsWith("/api"))
{
context.Request.Path = "/Home/SpaRedirect"; // attempts to redirect to the URL within the SPA
context.Response.StatusCode = (int)HttpStatusCode.OK; // Make sure we update the status code, otherwise it returns 404
await next();
}
});
...
}
So the newly added SpaRedirect to HomeController looks like this, and just stores the requested URL in ViewData...
public IActionResult SpaRedirect()
{
ViewData["RequestUrl"] = HttpContext.Request.Path;
return View("Index");
}
Then in Index.cshtml, just capture that requested url in session storage so we have it available on the client-side:
<script src="~/dist/main.js" asp-append-version="true">
sessionStorage.setItem("redirectAttempt", #ViewData["RequestUrl"]);
</script>
Then in your boot script file (the entry-point for your SPA), add something like:
let redirectAttemptUrl = sessionStorage.getItem("redirectAttempt");
if (redirectAttemptUrl) {
router.push(redirectAttemptUrl);
sessionStorage.removeItem("redirectAttempt");
}
Which just checks for the presence of a requested url, and then the SPA's router attempts to navigate to it (in the example above it is a vue-router), then removes it from storage.
So this way, if a user attempts to navigate directly to a URL by entering it in the url bar (or via a bookmark) the app will load and take them to the right place, IF it exists... which takes us to the last piece...
Finally, you have to handle "404s" within your SPA, which is done by adding a catch-all route to your routes defs that takes user to a 404 component page you set up, which for Vue would look like this:
// adding an explicit 404 path as well for programmatically handling when something is not found within the app, i.e. return this.$router.push('/404')
{ path: '/404', component: NotFound, name: '404', alias: '*' }, // remove alias to not show the actual url that resulted in our little 404 here
{ path: '*', redirect: '/404' }, // this is the catch-all path to take us to our 404 page
Caveat: I'm no expert so could be missing something, would love additional comments on how to improve this. One thing that this doesn't handle is if the user is ALREADY in the SPA and for some reason edits the URL directly to navigate to someplace else, it would still trigger a server call and full reload, which ideally wouldn't be the case, but this is a pretty trivial issue I'd say.

Resources