How to do mocks for Web tests? - asp.net

I want to write a few web tests (over WatiN/Selenium + CassiniDev web server) for my asp.net web application.
Problem I encountered is that I dont know what to do in such situations:
there is a page where user can click the button to call some third-party service. In my web test i want to create mock of this service, which will always return static value (some value in these test case and other value in other test case).
How can i do that?
Currently i use IoC/DI container Microsoft Unity. And my pages gets his dependencies in a manner described in http://msdn.microsoft.com/en-us/library/ff664622%28v=pandp.50%29.aspx.
The only solution that comes to my head is: place all dependencies in web.config for each test case and copy necessary web.config on SetUp of test. This solution completly painful!
Any ideas?

I use WatiN and Cassini-dev in my integration tests as well and have had to deal with similar issues. In my setup fixture I deploy my Asp.Net web application to a temporary folder in my test folder which allows me to play around with the configuration before starting up cassini-dev. I use Windsor for my CI which allows me to change injected components at the configuration level. You may also be able to acheive this with Unity.
If the service you are referring to is a web service you just mock out a web service using the interface you have been coding to.
Here are the steps that I take when running my integration tests:
Create a temp web directory
Publish the Asp.Net web application to the temp directory (I use MSBuild to do this)
Deploy temp database (Using MSbuild and database projects but could be done a number of ways)
Deploy temp membership database (see my blog post on how to do this in code)
Update the web.config of the deployed Asp.Net web application to point to the temp databases and change any other settings relevant for testing.
Start up the website using Cassini-Dev. I also hit the site with a http request so that I can verify the site is up before running any tests.
Run the tests.
After running the tests you should clean up.
Stop cassini-dev
Delete the temp hosting folder
Delete the temp databases. I use Sql server SMO objects that allow me to query the Sql Server which I use to delete up any old databases that have been left lying around after any previously failed test runs.
How to deploy a website using MSbuild in code
var properties = new Dictionary<string, string>
{
{"Configuration", isDebug ? "Debug" : "Release"},
{"WebProjectOutputDir", tempHostingDirectory.FullName},
{"DeployToDatabase", "true"},
{"OutDir", Path.Combine(tempHostingDirectory.FullName, "bin\\")}
};
using (var engine = new ProjectCollection(properties))
{
engine
.LoadProject(<web project path>, "4.0")
.Build(new[] {"Build", "ResolveReferences", "_CopyWebApplication"});
}
Unity configuration section usage: http://www.pnpguidance.net/Post/UnityContainerUnityConfigurationSectionAppConfigWebConfig.aspx
Generating asp.net membership database in code: http://bronumski.blogspot.com/2011/06/generating-creating-aspnet-application.html
Msbuild ProjectCollection on MSDN: http://msdn.microsoft.com/en-us/library/microsoft.build.evaluation.projectcollection.aspx

It sounds like you are trying to mock a web service.
Web services usually inherit from MarshalByRefObject, this means you can create a mock by inheriting from RealProxy to create a transparent proxy that pretends to be the webservice:
class Mock : RealProxy
{
public Mock()
: base(typeof(IStuff)) { }
public IStuff GetStuff()
{
return (IStuff)GetTransparentProxy();
}
public override IMessage Invoke(IMessage msg)
{
IMethodCallMessage message = (IMethodCallMessage)msg;
// the message object provides the MethodInfo that was called
// as well as the arguments.
// <Insert logic here>
return new ReturnMessage(new NotImplementedException("comming soon to a test near you ..."), message);
}
}
I belieave NMock2 uses RealProxy for it's mocks, so you should be able to use it to mock the web service instead.

Related

Handling development time web.config conflicts

I am looking for a way to handle this challenge: we are a geographically dispersed dev team using ASP.NET Web API and Angular to build a web app.
The thing that causes the grief is the fact that not all team members use the same database setup for their dev work. Yes, I know - I can use web.config transforms to set the proper connection strings for test, staging and production (and I'm already doing this) - but this is not what I'm talking about.
Due to reasons beyond our control at this time, we have
some developers working on a local SQL Server instance using server=(local);database=OurDB as their connection string
other developers using a central developer SQL Server in their location, using something like server=someserver.mycorp.com;database=OurDB
and a few exotic cases with yet other settings
Now every time someone commits a change to the Git repo, and happens to also change something in the web.config, his connection string is committed to the repo. So when I then go pull that latest commit, my settings to my local DB server are overwritten by this other guy's settings.
I am looking for a way to handle this - I was hoping I might be able to
hook into the Git pull process and automagically update the web.config connection string to my local needs whenever I pull something
somehow reference a connection string (or external config file) based on e.g. my currently logged in user's name or something like that
But I can't seem to find any way of doing this. I was wondering if I need to build a VS extension to handle this - any starters for that? Has anyone done something like this before and could share his code? (or has it up on Github)
The web.config configuration system used in ASP.NET is not flexible enough to support the more advanced scenario you have described. So, why use it? You could store the configuration in files within the repository, one per developer. Or they could be stored outside the repository or otherwise ignored.
The real trick is that most older applications don't have a single root that retrieve the configuration, so you have to refactor your application to utilize a flexible configuration system. For your staging/production environments you probably still want to use the config in web.config. The following code can give you a basic idea of one way to structure it:
public class MyApplicationConfiguration
{
public string MainConnectionString { get; set; }
}
public class ConfigurationRetriever
{
public MyApplicationConfiguration GetConfiguration()
{
// You might look for the absence or presence of an environment variable to determine this
bool isLocalDevelopment = IsApplicationLocalDevelopment();
var config = new MyApplicationConfiguration();
if(isLocalDevelopment)
{
config.MainConnectionString = Environment.GetEnvironmentVariable("MyApplication_MainConnectionString");
//or get it from a JSON file or XML file or config database
}
else
{
config.MainConnectionString = ConfigurationManager.ConnectionStrings["MainConnectionString"].ConnectionString;
}
}
}
Rather than rolling your own config building logic, you might refactor your application to leverage Microsoft.Extensions.Configuration. It's not just for .NET Core. It's for .NET Standard. So you can use it even in your legacy ASP.NET applications. For reading the web.config, you could probably use Microsoft.Extensions.Configuration.Xml. Or you can write your own adapter that pulls values out of ConfigurationManager. I did a basic test, and this worked as expected.

Setting project url in VS2015 ASP.NET 5 Web API application

I'm trying to create a Web API project and a client-side web project, where the web project can access the API via ajax. Currently my project looks like this:
I saw this answer on here: Setting app a separate Web API project and ASP.NET app, which explains how the project url can be set to localhost:[port]/api.
But for ASP.NET 5 projects, the properties only have 3 tabs (as opposed to the several found in ASP.NET 4 projects):
What I'm wondering is:
Do I have to set this option somewhere else? (i.e project.json)
How would this work when I publish? Ideally I'd want [websiteURL]/api to serve up my API, whereas that link explicitly put localhost:8080.
Is having these as two projects a good idea? I could easily put API and web in the same project, but I like the separation of client-side and server-side logic.
Any help would be appreciated!
First Point:
Generally speaking in ASP.NET 5, the routing defaults are very good and should work out of the box without much in the way of configuration. You can use configuration and/or attribute based routing in your application (with a detailed overview of both here), although my personal preference is for the attributed approach. Provided you have the following line in your Startup.cs file (which you should have in a new project):
app.UseMvc();
you should be able to route requests to your api controllers in the fashion required (i.e. "/api/...") simply by using [Route] attributes as below (example taken from a standard generated ASP.NET 5 Web API application)
[Route("api/[controller]")]
public class ValuesController : Controller
{
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
The above example will route any GET request made to "/api/values".
While this approach can be used to handle requests made to your api, in order to deliver the files needed for your front end javascript application/single page app, you will need to enable static file serving. If you add the following to the Configure method in your Startup.cs class:
app.UseStaticFiles();
this will allow your application to serve those static files - by default, these are served from the ‘wwwroot’ folder, although this can be changed in the project.json file if required. The files needed for your front end app should then be added to this folder. A tutorial on serving static files can be found here.
Second Point:
In my experience this will not be an issue when you publish your website - provided your server is set up correctly, you will not need to include the port when making a request - navigating to [yourwebsitename]/api/... will suffice.
Third point:
In my opinion this entirely depends on how large the project is likely to grow, although preference and opinion will vary from developer to developer. Generally speaking, if the project will remain small in scope then keeping both in a single project is perfectly ok, as unnecessary complexity is reduced. However it is also very useful as you have pointed out, to maintain a separation of concerns between projects. So aside from the organisational advantage of your approach, the respective dependencies of the two projects are/will be kept separate also.

Entity Framework 5, Migrations, MVC 4, update-database -force - best way to run on server

Visual Studio MVC 4 web app, SQL Server 2008 R2, Entity Framework 5
I have automatic migrations enabled (in configuration.cs):
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = false;
}
As a precaution, DataLossAllowed is disabled. What's the best way to run this migration manually, locally and on the server?
I get this error:
The "WebSecurity.InitializeDatabaseConnection" method can be called only once.
when I try to run it from package-manager.
It seems like manually running migrations, and making them automatic are incompatible?!
That error looks like you are initializing your database connection more than once rather than having an issue with Migrations.
WebSecurity.InitializeDatabaseConnection is what is called when you use the [InitializeSimpleMembership] attribute, which in MVC4 decorates the Accounts controller by default.
As you only want this to be called once, you should remove the attribute from your Account Controller and add a call to WebSecurity.InitializeDatabaseConnection to AuthConfig file, which is called on application start by global.asax. You could put it straight into the application start method in global.asax or make your own file but AuthConfig seems the best place for it in my opinion.
The code for it looks like this
WebSecurity.InitializeDatabaseConnection("DatabaseConnectionString","UserProfile","UserId","UserName", false);
renaming the DatabaseConnectionString to the name of your connection string in web.config
Also the false may be True if you are not using Db First.
Once you do that, you can remove the InitializeSimpleMembership file from the filters folder too.
This will ensure that InitializeSimpleMembership is only called once.

Some tests fail with "the communication channel with asp.net could not be configured" when running auto-generated tests against ASP.NET web services

The scenario:
Visual Studio 2010, ASP.NET web application
Create a web service class and give it some WebMethod-attributed methods
Have Visual Studio auto-generate unit tests for the methods, by right-clicking in the class definition and choosing Create Unit Tests...
Note that the generated code for each test includes this boilerplate:
// TODO: Ensure that the UrlToTest attribute specifies a URL to an ASP.NET page
// (for example, http://.../Default.aspx). This is necessary for the unit test to
// be executed on the web server, whether you are testing a page, web service, or
// a WCF service.
[TestMethod]
[HostType("ASP.NET")]
[AspNetDevelopmentServerHost("C:\\...\\ProjectName", "/")]
[UrlToTest("http://localhost:59733/")]
public void MethodNameTest()
Add in Default.aspx to UrlToTest, as requested by the comment:
[UrlToTest("http://localhost:59733/Default.aspx")]
Run all tests in the class
The problem:
Inconsistently, some tests fail with
The communication channel with ASP.NET could not be configured. Requested Service not found
Which tests fail and which tests pass can vary from run to run. There appears to be no pattern to the failures, but it's never the case that all successfully run.
What's going wrong?
Is it the case that the page you've specified in UrlToTest always performs a Response.Redirect on load? Because if it is, this will the the cause of the failures you're seeing.
Change the URL specified in UrlToTest to that of a page that does not perform a Response.Redirect, and your tests should run fine.
Run your tests through the Resharper unit test runner. That also gets rid of the problem for me. That also avoids having to reload the web.config multiple times and feels quicker as well.

eventlog not working in an asp.net application

from an ASP.Net 3.5 web application, I'm trying to log messages to the Windows EventLog.
I first tried with the EntLib Logginh block, but when this failed I tried with the EventLog class directly. It failed too. They do not throw any exception... the just don't write the message. EntLib did write the message to a file, but not to the Windows EventLog.
Here is my code:
public static void LogMessage(string title, string message){
//EventLog log = new EventLog();
//log.Source = LOG_SOURCE;
//log.WriteEntry(message, EventLogEntryType.Error);
//EventLog.WriteEntry(LOG_SOURCE, message);
LogWriter writer = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();
writer.Write(message);
}
I create the log & source in an installer class. Let me know if I should place that code here. The log is created correctly, since I can see it in the EventViewer. The source is created correctly, since I can see it in the "EventLog\MyLog" folder at the regedit.
I've been reading and there is an article stating following line could help:
EventLogPermission perm = new EventLogPermission(EventLogPermissionAccess.Administer, ".");
perm.PermitOnly();
but it didn't.
If it helps, my code structure is as follows:
Class library project (here is the LogMessage method)
Class Library project (here are the methods which catch exceptions and call LogMessage)
ASP Net web application project (web pages. This layer calls layer #2. Here is my installer class too)
Web setup project (this has custom actions pointing to web setup project output)
Could you please help to figure out what's happening???
Thanks
I found the following resource: "http://www.netframeworkdev.com/net-base-class-library/trouble-writing-to-eventlog-16723.shtml", so it seems it is not possibly to create custom logs from ASP... still investigating
Try giving the Network Service account the appropriate permissions

Resources