Unit Testing ASP.net Web Site Project code stored in App_Code
Asked Answered
P

8

48

I have an ASP.net Web Site Project (.net 3.5). Currently all of the non-code behind code files (including Linq2Sql stuff, data contexts, business logic, extension methods, etc) are located in the App_Code folder.

I am interested in introducing Unit Testing (using nunit) in at least some sections of the project moving forward. Any Unit Testing that I would be doing would need to have full access to all of the code that is currently located in the App_Code folder. I have done some initial reading so far, and the consensus seems to be:

  • This will not be possible given my current setup
  • Unit testing requires referencing classes that are part of a compiled dll, and a Web Site Project by definition only compiles at run time.
  • In order to proceed, I will need to either convert my entire project to a Web Application, or move all of the code that I would like to test (ie: the entire contents of App_Code) to a class library project and reference the class library project in the web site project. Either of these will provide access to the classes that I need in compiled dll format, which will allow me to Unit Test them.

Is this correct? Or is there another way that I can Unit Test without restructuring/refactoring my entire project?

Plafond answered 29/7, 2009 at 7:29 Comment(0)
E
19

Your conclusions seem correct. I would vote for moving functionality into one or several class library projects, since that may open the door for reusing the same functionality in other projects as well.

Estrin answered 29/7, 2009 at 7:38 Comment(0)
E
24

My shop has finally worked through an answer for this for our MVC project. And I want to share it as I chased a lot of dead ends here on StackOverflow hearing a lot of people say it couldn't be done. We do it like this:

  • Open the MVC folder "as a website, from local iis" which gets intellisense and debugging working properly
  • Add a unit test project that lives in our source controlled directory
  • Add a pre-build step to the TEST project, since we can't add one to a project that is open as a website. Imagine website is \FooSite and our test project is \FooSite.Tests. The compiled app code will end up in FooSite.Tests\FooSite_Precompiled\bin.
  • *
<Target Name="BeforeBuild">
     <AspNetCompiler VirtualPath="FooSite" TargetPath="$(ProjectDir)\FooSite_Precompiled" Force="true"
 Debug="true" />   </Target>
  • Add a reference to the FooSite_Precompiled/bin/App_Code.dll in your test project.
  • Boom that's it. You can have your cake and eat it too. Every time you click Build in your solution you call the aspnet_compiler.ext tool on your website csproj (which does still exist) which is able, unlike MSBuild, to compile app_code, and the Debug="true" allows you step into the app_code.dll code when debugging your unit test. And you only need to Build when you're running updated unit tests. When you're looking at the effects of your change on the page, you just Change Code/Save/Refresh Page since the app_code folder dynamically compiles when called from your web server.
Ephesus answered 30/4, 2012 at 15:43 Comment(4)
This should be the accepted answer. I'll add that it's quite slow to run the AspNetCompiler when building the test project though, so I'm using an Exec task to run a build of just the App_Code files.Autosuggestion
How did you set that up exactly? An exec task of calling a particular msbuild command? Could you share an example?Ephesus
We use a proprietary tool which is called with an Exec task, but it should be possible by either calling CSC directly, or with a separate project file which includes every source file in the App_Code folder. Funny you should ask this now as I've recently run into a problem with this system in Visual Studio 2012. I've posted a question to see if anyone can help resolve the issue, and included an example project with a similar setup to the one I'm using: #13139320Autosuggestion
Just wanted to come back to this and say we ripped out all the app_code except for the global html helpers which need to run there. This approach was a pain due to the separate solutions, devs kept missing that a test project even existedEphesus
E
19

Your conclusions seem correct. I would vote for moving functionality into one or several class library projects, since that may open the door for reusing the same functionality in other projects as well.

Estrin answered 29/7, 2009 at 7:38 Comment(0)
H
12

We have this issue at my company (My boss doesn't like DLLs, some rubbish about versioning...)

We have two ways round it that we use frequently:

1) Get the CI tool to do the unit testing: We use TeamCity which has a pretty tight NUnit integration, and our solution builds quick enough (and has few enough tests) for this to be a valid option.

2) Manually precompile and unit test the resulting binaries: It's perfectly possible to run the ASP.net compiler / MSBuild from the command line (as if you were doing a 'Publish' build) and just unit test the resulting binaries.

However, if you have the option of segregating the code into binaries (class libraries) or just using a web application, I'd suggest that as a better alternative.

Hartz answered 21/6, 2010 at 11:32 Comment(1)
Versions of dlls before .Net was a nightmare. I believe this was nicknamed "dll hell". With .Net this isn't really a problem any more.Pinelli
B
6

Should anyone find themselves implementing Brian's solution, here's a Website.targets file you can include in unit testing solution. It (re)compiles website only when App_Code changes. Just add something like

  <PropertyGroup>
    <WebsiteName>MyWebsite</WebsiteName>
    <WebsitePath>..</WebsitePath>
  </PropertyGroup>
  <Import Project="$(ProjectDir)\Website.targets" />
  <Target Name="BeforeBuild" DependsOnTargets="CompileWebsite">
  </Target>

to your .csproj, customizing WebsiteName and WebsitePath and you should be ready to go. Website.targets:

<?xml version="1.0" encoding="utf-8"?>
<!--
    Target that compiles Website's App_Code to be used for testing
  -->
<Project DefaultTargets="CompileWebsite" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <AppCodeFiles Include="$(WebsitePath)\$(WebsiteName)\App_Code\**\*.*" />
  </ItemGroup>
  <Target Name="CompileWebsite" Inputs="@(AppCodeFiles)" Outputs="$(ProjectDir)\PrecompiledWeb\bin\App_Code.dll">
    <AspNetCompiler VirtualPath="$(WebsiteName)" PhysicalPath="$(WebsitePath)\$(WebsiteName)" TargetPath="$(ProjectDir)\PrecompiledWeb" Force="true" Debug="true" />
  </Target>
  <Target Name="CleanWebsite">
    <RemoveDir Directories="$(WebsitePath)\$(WebsiteName)\PrecompiledWeb" />
  </Target>  
</Project>
Bracey answered 4/6, 2013 at 8:17 Comment(1)
This worked great for me, the only thing I would add is that if you want everything to build in a TFS build, then I what I do is check in the generated DLL, and since I don't want to check in the whole "PrecompiledWeb" folder, I copy it to a separate folder as part of the build process: <PropertyGroup> <PreBuildEvent>copy "$(ProjectDir)\PrecompiledWeb\bin\App_SubCode_CSCode.dll" "$(ProjectDir)\Library\App_SubCode_CSCode.dll"</PreBuildEvent> </PropertyGroup>Damal
C
2

It looks like this is possible whilst still using App_code, but I would either move this logic out to its own class library project or change the project type to Web Application, as Fredrik and Colin suggest.

I always create my own ASP.NET projects as Web Application projects not Websites.

Cards answered 29/7, 2009 at 8:6 Comment(2)
Thanks for the link. Definitely looks like an ugly solution though.Plafond
Yes absolutely- hence better to go down one of the more sane routes.Cards
C
1

And as the OP stated it's also possible to move to a Web App project, which i would say is cleaner as well, your pages can stay in the wep app project, you will have them in 1 DLL (testable). All your business logic etc. goes in a separate class library / libraries.

Chrysotile answered 29/7, 2009 at 8:0 Comment(1)
Although this knowledge is useful, I don't think this is a good answer because it is stated in the question that the OP knows how to do this. Also, I feel that this question is aimed at people who already know this.Railey
R
0

It is possible to unit test classes stored in the App_Code folder without converting your project to a Web App or moving your classes to a Class Library project.

All that is necessary is setting the code files' Build Actions to Compile. This will cause Debugging and Unit Testing your website to output a .dll file.

Now when you reference your website project from the unit test project, the classes in the app_code folder will be visible.

NOTE:

Setting your .cs files' Build Action to Compile will cause your website to generate a .dll file on debugging and unit-testing. The .dll file will cause problems when you debug your website because IIS will now find your code in two places, the bin and the App_Code folder and will not know which one to use. I currently just delete the .dll file when I want to debug.

Railey answered 26/5, 2016 at 21:6 Comment(1)
I do not understand why my answer was down-voted. The OP was wrong in thinking that the only way to unit test is to move the code to a class library project or convert the solution to a web-app and my answer shows how to accomplish unit testing while keeping the code in the app_code folder. If my answer is in a poor format, I hope the down-voter will explain what they dislike about it.Railey
V
0

I had to change Brian White's solution by adding the PhysicalPath attribute. In addition I am not using the Default Web Site and had to change the VirtualPath property to my website name.

<Target Name="BeforeBuild">
    <AspNetCompiler VirtualPath="myIISsitename.com" PhysicalPath="$(SolutionDir)MySiteFolder" TargetPath="$(ProjectDir)\MySite_Precompiled" Force="true" Debug="true" />
</Target>

The resulting dll will be at MySite_Precompiled\App_Code.dll

Venetic answered 19/7, 2016 at 1:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.