ASP.NET unit testing Windows7/IIS7 - asp.net

Spent several hours today trying to write some unit tests against an ASP.NET project. It's Visual Studio 2010.
Using Windows 7 Enterprise with IIS7.
Steps I took were:
Added a new test project to the solution
Opened a class file as part of the web site (Member.vb)
Right clicked within the class file and "Generate unit tests"
Select the methods I wish to generate stubs for, choose to add to my test project, click OK
Open up the generated MemberTest.vb file in the test project, click within one of the generated tests, click "Run tests in curent context"
When following these precise steps on my Windows XP Professional with IIS6 machine it works fine.
However on the Windows 7 Enterprise machine on IIS7 I get:
The URL specified ('http://localhost/MyProject') does not
correspond to a valid directory. Tests configured to run in ASP.NET in
IIS require a valid directory to exist for the URL. The URL may be
invalid or may not point to a valid Web application.
So what's going on, I can confirm I can browse to http://localhost/MyProject and it displays perfectly.
I feel sure I'm missing some sort of config in Windows/IIS but I'm really at a loss.
Generated test method:
<TestMethod(), _
HostType("ASP.NET"), _
UrlToTest("http://localhost/MyProject")> _
Public Sub MyMethodTest()
Dim target As Member_Accessor = New Member_Accessor() ' TODO: Initialize to an appropriate value
Dim CurrentVal As Short = 0 ' TODO: Initialize to an appropriate value
Dim expected As Short = 0 ' TODO: Initialize to an appropriate value
Dim actual As Short
actual = target.MyMethod(CurrentVal)
Assert.AreEqual(expected, actual)
Assert.Inconclusive("Verify the correctness of this test method.")
End Sub
(Cross-posted at ASP.NET Forums)

This could be a permissions issue.
If you're using the default directory (C:\users\\Documents\Visual Studio 2010\Projects), the app identity pool does not have permissions there. You'd have to create a project in something like C:\webs and make sure app pool identity has permission to the folder.
Refer to Rick Anderson's blog post at http://blogs.msdn.com/b/rickandy/archive/2011/04/22/test-you-asp-net-mvc-or-webforms-application-on-iis-7-in-30-seconds.aspx and see if that helps.

If you have not done unit testing before, I would really recommend that you start by just testing the functionality of your classes as cleanly as possible. Try to break you you functionality into small pieces that can be tested individually without and dependencies to the web context.
Have a look at this question for an idea about What is unit testing
Here is an MSDN Magazine article about testing
You can also have a look at this Blog. The examples are using NUnit but the principal is the same if you are using MSTest.
I can also recommend Roy Osheroves Book Art of unit testing
In you case if the Member class does not have dependencies to web context you don't need the IIS and could instead just do something like this:
<TestMethod()> _
Public Sub MyMethodTest()
Dim target = New Member()
Dim CurrentVal As Short = 0 ' TODO: Initialize to an appropriate value
Dim expected As Short = 0 ' TODO: Initialize to an appropriate value
Dim actual As Short
actual = member.MyMethod(CurrentVal)
Assert.AreEqual(expected, actual)
End Sub

I ran into the same problem today. After some research, I found this thread which suggested I check my event log. Upon doing that, I discovered numerous errors similar to the following:
(QTAgent32.exe, PID 12348, Thread 61) WebSites.GetWebServer: failed to
create AspNetHelper:
Microsoft.VisualStudio.Enterprise.Common.AspNetHelperException: The
website metabase contains unexpected information or you do not have
permission to access the metabase. You must be a member of the
Administrators group on the local computer to access the IIS metabase.
Therefore, you cannot create or open a local IIS Web site. If you
have Read, Write, and Modify Permissions for the folder where the
files are located, you can create a file system web site that points
to the folder in order to proceed. --->
System.Runtime.InteropServices.COMException: Unknown error
(0x80005000)
That lead me to this blog post which seems to have resolved the issue.
I just needed to go to "Turn Windows features on or off" and add IIS 6 Management Compatibility and all four subcomponents. I'm running Windows 7 Home Premium which doesn't have the Windows Authentication option, but that didn't seem to be an issue. Give it a shot and see if that resolves the issue for you.

You may need to enable "Use IIS" in the project properties, then click "Create Virtual Directory". Do you have IIS Express installed?

Related

Locating the source DLL behind a COM+ ProgId

I've never really had to debug Classic ASP, so this is a little rough and most likely a poor question, but I have done as much research as I could before asking.
I have a request to identify what code prints to a printer, and re-use that code in a new page that someone has built.
While trying to identify that, I've stumbled on a few things that I don't understand, but namely one big one.
The gist is, people can order cookies from the cafeteria, and when they submit, it shows a confirmation page and that order is sent to a printer.
To get a list of cookie options, there's a Server Object created, and from there a method exists, but I cannot identify where it is or where I should be looking. Here's the code:
<%
On error resume next
Const CATAGORY_COOKIE = 1
Dim cookieNames
Dim objCookie
Dim Count
Set objCookie = Server.CreateObject("CookieOrder.CookieRequest")
if objCookie Is Nothing then
Response.Write "Error"
Response.End
End if
cookieNames = objCookie.getAvailable_Item_Names(CATAGORY_COOKIE)
Count = objCookie.Count
Dim sz
sz = Split(cookieNames, ";")
Set objCookie = Nothing
%>
How do I identify what the Server Object is? There's a .dll file that contains binary, but I'm not familiar with how that could be utilized.
I have tried to follow the browser dev tools, but they really haven't been too helpful in this aspect.
I am hoping that learning how this code is executing or where it's being executed I will figure out my other problems.
Bit of background
The project is using a COM+ component. These are defined in Classic ASP using the syntax;
Set obj = Server.CreateObject("[insert COM+ ProgId]")
In this project you are using a component registered with the ProgId
CookieOrder.CookieRequest
There are many out of the box COM+ components available to Classic ASP that provide a lot of common functionality such as;
Visual Basic Scripting Runtime
ActiveX Data Objects
There is also the ability to create COM+ components for use with Classic ASP using languages common to the time like Visual Basic, Visual C++ and more recently using the .NET Framework (C#, VB.NET).
How to locate COM+ libraries
NOTE: Please be careful when accessing the registry as modifying or deleting keys could lead to a corrupt operating system.
Also for the purposes of this guide will use the Scripting.Dictionary ProgId.
The key is using the ProgId to find an elusive COM+ library.
Start %SystemRoot%\system32\regedit.exe (will work in most Windows Operating Systems)
Navigate to the HKEY_CLASS_ROOT hive and select it, then press Ctrl + F to open the Find dialog box.
In Find what type the ProgId in this case Scripting.Dictionary and make sure in look at only Key is checked then press Find or Find Next.
If a ProgId key is found expand to the key and locate the CLSID key which contains a (Default) REG_SZ with the value of the CLSID in the case of this example {EE09B103-97E0-11CF-978F-00A02463E06F}. Double click this value to bring up the Edit String dialog copy the value into your clipboard.
Return to the HKEY_CLASS_ROOT key and use Find to search for the CLSID value which in this example is {EE09B103-97E0-11CF-978F-00A02463E06F} and again make sure Look at has only Key checked then press Find or Find Next.
If the key is found expand and locate the InprocServer32 key in it you will find the location of DLL in the (Default) REG_SZ value. In this example that is C:\Windows\System32\scrrun.dll (this will be different depending on installation location and OS)
What about decompiling?
There's a lot of assumption in the comments about the compiler used to compile the DLL (mainly .NET), but the best way to check is to use one of the many programs out there in the public domain designed for this purpose.
There is a specific question on SO that deals with this;
Answer by #simon-mᶜkenzie to Identifying the origin of a DLL

CultureInfo values differ between applications for the same culture. Is this a bug?

I have a strange issue occurring on my Windows 8 dev box. The following line of code results in two different values for the NumberFormat.NumberDecimalSeparator when comparing an ASP .NET application running Kentico and a console application (both running on .NET 4.0).
var culture = new System.Globalization.CultureInfo("en-ZA");
var separator = culture.NumberFormat.NumberDecimalSeparator;
The value of seperator:
Kentico application: "," <- comma
Console application: "." <- period
The correct output for my regional setting is a period.
How is this possible? When I first picked up a formatting issue for decimal numbers, I thought it may have been a Kentico bug, however this test indicates otherwise. How is it possible that a new instance of CultureInfo for a specific locale returns an instance that differs across applications?
Jason Evans's comment pointed me in the right direction. See the link he posted: ASP.NET application doesn't reflect Regional settings
It turns out that regional settings are stored per user in Windows. This is something I should have been aware of. Updating the application pool to run as myself produced the same result across both applications.
To be fair, what is still confusing is how Network Service (the account the application pool was running under) came to have the incorrect value. I'm not even sure how I'd rectify that.
Edit:
If you need to update the regional settings for reserved accounts. You have two options.
Control Panel > Regional Settings > Click the administrative tab and then select "Copy Settings". On the screen that launches, ensure you check "Welcome Screen and system accounts". Older versions of Windows are similar I believe.
For the brave. Registry: HKEY_USERS > SID... > Control Panel > International. The security identifier for Network Service is: SID: S-1-5-20.
Ensure you restart the application pool for settings to take effect.
EDIT: Note that this wasn't the issue - but in other cases it could be, so I'm leaving it here for posterity.
I strongly suspect that the console application isn't running on .NET 4.0. It's easy enough to verify that using Environment.Version though. Here's a short console app and results via two different versions of the framework:
using System;
using System.Globalization;
class Test
{
static void Main()
{
CultureInfo culture = new System.Globalization.CultureInfo("en-ZA");
string separator = culture.NumberFormat.NumberDecimalSeparator;
Console.WriteLine("Version: {0}", Environment.Version);
Console.WriteLine("Separator: {0}", separator);
}
}
Compiling against .NET 4.5:
Version: 4.0.30319.18010
Separator: ,
Compiling against .NET 2.0:
Version: 2.0.50727.6400
Separator: .
Try this code in both applications, and see what versions they're running.

TFS SDK assembly error after moving code

We recently did some code reorganization for our ASP.NET web site solution and I've run into an unfriendly issue with the TFS 2010 SDK assemblies that I haven't been able to figure out.
We have a small class (I included the functions using TFS SDK below) that retrieves all the TFS changeset comments since the last time we deployed the website. The web site project has the following references to use the TFS SDK:
Microsoft.TeamFoundation.Client
Microsoft.TeamFoundation.VersionControl.Client
The class used to live in the App_Code folder of the project and the following TFS assemblies were deployed in the bin folder of the site:
Microsoft.TeamFoundation.Client.dll
Microsoft.TeamFoundation.Common.dll
Microsoft.TeamFoundation.Common.Library.dll
Microsoft.TeamFoundation.dll
Microsoft.TeamFoundation.VersionControl.Client.dll
Microsoft.TeamFoundation.VersionControl.Common.dll
This worked properly with no errors when deployed to the site.
We moved this class (along with several others) into a separate class library and removed it from the App_Code folder of the site, changing all appropriate assembly references in Visual Studio for the projects. Now when it is deployed, we get the following error on any page we hit on the site:
Could not load file or assembly 'Microsoft.TeamFoundation.WorkItemTracking.Client.Cache, Version=10.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. An attempt was made to load a program with an incorrect format.
I can run from my localhost and development workspace without any issues, so something with the build seems to be amiss. After examining the build that TFS pumped out, three more assemblies are being included in the build for the TFS SDK that weren't there before:
Microsoft.TeamFoundation.WorkItemTracking.Client.Cache.dll
Microsoft.TeamFoundation.WorkItemTracking.Client.DataStore.dll
Microsoft.TeamFoundation.WorkItemTracking.Client.RuleEngine.dll
I can't figure out why these are being pulled into the build now. We don't directly reference these assemblies in any project. The code for pulling the comments from TFS didn't change, only it's assembly location. No TFS upgrades were performed. Bottom line - we added a class library where the code now lives and it seems to want some other assemblies that it didn't want before when it lived in the App_Code folder. My best guess is that TFS pulls these in as dependent assemblies for the others during the build, but I'm not sure why.
Hits online for similar errors generally revolve around 32/64 bit issues. I checked Config Manager and all the projects in the solution are set to use Any CPU, which seems right to me, and we have 32-bit applications enabled in IIS on the server.
My apologies for the length of this post, but I wanted to provide what I thought were the pertinent details. Any thoughts would be greatly appreciated. Thanks.
Public Function GetChangesSinceDeployDate(ByVal lastDeployDate As DateTime) As List(Of TFSChange)
Dim tfs As New TfsTeamProjectCollection(New Uri("http://tfs.proviadoor.com:8080/tfs/entrylink"))
tfs.EnsureAuthenticated()
Dim vcs As VersionControlServer = CType(tfs.GetService(GetType(VersionControlServer)), VersionControlServer)
Dim versionFrom As VersionSpec = GetDateVSpec(lastDeployDate)
Dim versionTo As VersionSpec = GetDateVSpec(Now)
_changeList = New List(Of TFSChange)
Dim changeSetIds As String = ""
For Each projectPath As String In _projectPaths
Dim results As IEnumerable = vcs.QueryHistory(projectPath, VersionSpec.Latest, 0, RecursionType.Full, "", versionFrom, versionTo, Integer.MaxValue, False, True)
For Each chgSet As Changeset In results
If Not chgSet.ChangesetId.ToString().InList(changeSetIds) Then
_changeList.Add(New TFSChange(chgSet.ChangesetId, chgSet.Committer, chgSet.CreationDate, chgSet.Comment))
changeSetIds.Append(chgSet.ChangesetId.ToString(), ",")
End If
Next
Next
Dim sortedList = From chg As TFSChange In _changeList _
Select chg _
Order By chg.CommitUser, chg.ChangeDate Descending
Return sortedList.ToList()
End Function
Private Function GetDateVSpec(ByVal versionDate As DateTime) As VersionSpec
Dim dateSpec As String = String.Format("D{0:yyy}-{0:MM}-{0:dd}T{0:HH}:{0:mm}", versionDate)
Return VersionSpec.ParseSingleSpec(dateSpec, "")
End Function
The message "program with an incorrect format" does suggest a 32/64-bit issue.
The problem is that "Any CPU" code will launch in the highest bit-ness it can: in your case, 64-bit, because all of the assmemblies referenced at launch time are 64-bit (or at least Any CPU).
Some combination of lazy assembly loading/JIT in .NET means that the TFS assemblies aren't actually loaded until you hit your method call. By this time, your application is already committed to run as 64-bit, and so loading the 32-bit TFS assemblies fails - you can't mix bit-ness in one process. Permitting 32-bit apps in IIS isn't enough: you have to ensure that your application really does run as 32-bit by changing the compilation settings. In this case "Any CPU" is actually the culprit, because it's allowing your code to launch as 64-bit.
I say "some combination" because I'm not sure of the precise detail, but I have seen exactly the same issue in a console application, compiled as Any CPU and referencing the TFS DLLs. The solution was to compile the entrypoint assembly as 32-bit. Where this same code was consumed in a web service, we ended up shelling out to a separate process so that the web app wasn't reduced to 32-bit too.

How do I force compilation of ASP.NET MVC views?

I have a Windows Azure web role that contains a web site using ASP.NET MVC. When an HTTP request arrives and a page is first loaded the view (.aspx or .cshtml) is compiled and that takes some time and so the first time a page is served it takes notable longer than later serving the same page.
I've enabled <MvcBuildViews> (described in this answer) to enforce compile-time validation of views, but that doesn't seem to have any effect on their compilation when the site is deployed and running.
Azure web roles have so-called startup tasks and also a special OnStart() method where I can place whatever warmup code, so once I know what to do adding that into the role is not a problem.
Is there a way to force compilation of all views?
Take a look at Precompiled Razor Views by David Ebbo
Why would you want to do that?
One reason to do this is to avoid any runtime hit when your site
starts, since there is nothing left to compile at runtime. This can be
significant in sites with many views.
Also, you no longer need to deploy the cshtml files at all, resulting
in a smaller deployment file set.
Another cool benefit is that it gives you the ability to unit test
your views, which has always been something very difficult with the
standard runtime compilation model. I’ll cover that in more details in
a future post.
Turns out there's ASP.NET Precompilation that can be performed using ClientBuildManager.PrecompileApplication and mimics the on-demand compilation behavior, but just compiles every page. Tried it - the first load looks notably faster.
The non-trivial part is what to pass as ClientBuildManager constructor parameters. The solution is to enumerate all .Applications of the Site object and for each item in .Applications enumerate all .VirtualDirectories and use Path and VirtualPath from each item as parameters to ClientBuildManager constructor.
Is this an initial-load issue or a steady-state issue? One issue seen is that of app pool recycling, which defaults to 20 minute timeout. If you disable timeout (or set it to something large), is that a valid workaround?
Here's another SO answer discussing AppPool timeout and how to disable it. Basically:
%windir%\system32\inetsrv\appcmd set config -section:applicationPools -applicationPoolDefaults.processModel.idleTimeout:00:00:00
Add this to OnStart:
using (var serverManager = new ServerManager())
{
string siteName = RoleEnvironment.CurrentRoleInstance.Id + "_" + "Web";
var siteId = serverManager.Sites[siteName].Id;
var appVirtualDir = $"/LM/W3SVC/{siteId}/ROOT"; // Do not end this with a trailing /
var clientBuildManager = new ClientBuildManager(appVirtualDir, null, null,
new ClientBuildManagerParameter
{
PrecompilationFlags = PrecompilationFlags.Default,
});
clientBuildManager.PrecompileApplication();
}
If you use the Publish functionnality of Visual Studio, there is a much simpler option :
On the Publish dialog > Settings pane, expand File Publish Options and check Precompile during publishing then click configure. On the Advanced Precompile Settings dialog box, uncheck Allow precompiled site to be updatable.
source: https://msdn.microsoft.com/en-us/library/hh475319.aspx

Server.CreateObject Failed with chiliupload component

I get the following error with a legacy asp application that I have been asked to help out with.
Server object error 'ASP 0177 : 800401f3'
Server.CreateObject Failed
/site_manager/image_upload.asp, line 27
800401f3
The line ofcode that throws the error is shown below:
Set fbase = Server.CreateObject("chili.upload.1")
As you ahve probably guessed oldschool asp isn't my strong point but from the research I have done it seems as if a component hasn't been registered on the server (I only have FTP access).
What component needs to be regsistered?
Thanks for the help...
You're missing the registration of the DLL that creates the chili.upload.1 object. Are you trying to run this on a Linux machine?
You need to register the Sun Chili!Soft ASP components. Here's the manual on this from 2003:
http://ns7.webmasters.com/caspdoc/html/running_the_setup_program_sun_chili_soft_asp_for_windows.htm. Note that this only works if you still have the original setup. Otherwise you're out of luck. Sun Chili!Soft ASP is no longer available and very, very dead.
If you're just interested in file upload functionality on ASP, I can recommend Free ASP Upload. It requires no registration of any components and generally works. I can also recommend this article on the topic of ASP uploads. If you're willing to shell out some money there are hundreds of components that do the same thing too.
Register the DLL on your computer, and then do this:
Locate and then click the following registry subkey:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\MAIN\
FeatureControl\FEATURE_IGNORE_ZONES_INITIALIZATION_FAILURE_KB945701
Note If the FEATURE_IGNORE_ZONES_INITIALIZATION_FAILURE_KB945701 subkey does not exist, you must manually create it. If you're using a 64 bit OS, you may need to use HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Internet Explorer\MAIN\ FeatureControl\FEATURE_IGNORE_ZONES_INITIALIZATION_FAILURE_KB945701 instead
Right-click FEATURE_IGNORE_ZONES_INITIALIZATION_FAILURE_KB945701,
point to New, and then click DWORD Value
Type w3wp.exe to name the new registry entry, and then press ENTER.
Right-click w3wp.exe, and then click Modify.
In the Value data box, type 1, and then click OK.
After setting this registry key, a simple app pool restart will apply the change. No longer will your .NET COM components randomly stop working with no real solution except shuffling application pools!

Resources