How to use web.config when unit testing an asp .net application
Asked Answered
B

7

76

I am starting out with unit testing, I have a method that uses the web.config for a connection string.

I was hoping to be able to use

[DeploymentItem("web.config")]

to get the web config file, this still leaves me with null reference exceptions (that'd be what I write my next test for).

How do I use the config file included with the project I am trying to test?

I am using the testing framework included in VS 2008, if that makes any difference.

Thanks

Brendabrendan answered 5/2, 2009 at 14:57 Comment(2)
Do you put your unit test code in the same project as your application code?Samekh
+1, I had the same question today. Sadly I still can't find the answer I was looking for because what I wanted to do was add a link to the current web.config in my web app and use those values as I write the code, otherwise, adding an app.config for my tests would not test my web app configuration, that's a part of the information I want to test while I develop my app. However, it makes sense not to mix tests with code but that's sometimes difficult to do.Ellisellison
S
114

Unit test projects should have their own config file.

On a test project you can choose Add, New Item, Application Configuration File.

This file will behave exactly like a web.config, but then for your unit tests.

Samekh answered 5/2, 2009 at 15:2 Comment(2)
I've tried what you suggested, I added a new config file into my test project. The connection string is in place, but when running my tests I always see null on the line var sqlCon = new SqlConnection(ConfigurationManager.ConnectionStrings["SqlServer"].ConnectionString); What might I be missing?Brendabrendan
FWIW , I posted a response to a similar question (https://mcmap.net/q/266904/-nunit-tests-looking-for-assembly-name-dll-config)[here] . The gist is you can have VS overwrite the config files that are copied when you build the test project .Isom
C
18

You can load in a web.config or app.config from any location using OpenMappedExeConfiguration. Make sure System.Configuration is added to your project's References.

ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap()
fileMap.ExeConfigFilename = @"c:\my-web-app-location\web.config"

Configuration config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
string connectionString = config.AppSettings.Settings["ConnectionString"].Value;

Here is the web.config, pretty standard.

<?xml version="1.0"?>
<configuration>
  <configSections>
  </configSections>
  <appSettings>
    <add key="ConnectionString" value="Data Source=XXXX;Initial Catalog=XXX; Trusted_Connection=True;"/>
  </appSettings>
</configuration>

Update on 2017-09-29

I have come up a class to make it easier for reading AppSetitngs from file. I got the idea from Zp Bappi.

public interface IAppSettings
{
    string this[string key] { get; }
}

public class AppSettingsFromFile : IAppSettings
{
    readonly Configuration Config;

    public AppSettingsFromFile(string path)
    {
        var fileMap = new ExeConfigurationFileMap();
        fileMap.ExeConfigFilename = path;
        Config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
    }

    public string this[string key]
    {
        get
        {
            return Config.AppSettings.Settings[key].Value;
        }
    }
}

Here is how to use the class.

IAppSettings AppSettings = new AppSettingsFromFile(@"c:\my-web-app-location\web.confg");
string connectionString = AppSettings["ConnectionString"];
Cimbalom answered 8/5, 2014 at 18:39 Comment(0)
M
8

Copy your web.config file into the "/bin" folder and rename it into "AppName.dll.config".

Where "AppName" - is the name of the resulting assembly.

I used this hack many times.

Microcyte answered 5/8, 2011 at 13:11 Comment(4)
It didn't work for me. Unit Test does not automatically know about ConnectionString in the config file. I have to write some custom code to read it from the config.Deering
Adding a simple app.config like in the accepted answer should work in most cases. This is a hack for some weird configs (like "ad-hoc test" in TestDriven)Microcyte
It worked for me with the following setting in the test project's .proj file: <ItemGroup> <None Include="..\MyProject\Web.config"> <Link>$(TargetFileName).config</Link> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> </ItemGroup> ThanksDeering
Changing the Properties of your configuration file would copy the file for you at compile time. Set Copy to Output Directory to Copy AlwaysDisconnection
G
4

You will want your results to be well-defined and repeatable. To to this, you'll need to be working against known data so that you can clearly define both your normal cases and your boundary cases. In my work, this is always a specific server and dataset so the Unit testing module has the connection string built in. Others prefer using a connection string out of the Unit Testing project. I've never seen anyone recommend the use of the web site's config file! (development or otherwise)

Grad answered 5/2, 2009 at 15:7 Comment(3)
That makes a lot of sense, so what's the solution to getting the connection strings into my app for testing?Brendabrendan
Either hard code the Connection String that is specific to your testing environment (this has a bad smell but it is really quite practical as it has nothing to do with deployable code) or use a Config file defined specifically for your unit testing module as Gerrie suggested.Grad
Just to emphasize: I am not suggesting that you ever hard code a database connection string in code that will be deployed! I have a specific database on a specific server that I always use for unit testing. Before testing, I run a script that ensures that the db is in the appropriate state.Grad
H
3

If you need a connection string, you are not writing a unit test (assuming that you are using the connection string for going to database). Unit tests are not supposed to interact with outside environment. You will want to run all of them after each check in so they better run at the speed of light.

For a unit test, you will want to isolate your code from your database. Modify your tests (and the code you are testing if necessary) so that you will not need to go to database for testing them.

Hanako answered 5/2, 2009 at 15:38 Comment(14)
So are you suggesting that I don't test any code that accesses a database?Brendabrendan
he's suggesting you don't test it against the database defined in your web.configGeneral
No. Your code must never access to the database (unless you are writing a database framework). Instead, you pass an object to your code, which accesses to database. In your unit tests, you mock that object, in production, you use the actual database accessing class.Hanako
buyutec is right, when you this your tests aren't unit tests anymore, but integration tests.Samekh
I just flat out disagree with this on a fundamental level. It takes Unit Testing out of the realm of practical, hands-on tool for improving your code, and turns it into an abstraction hemmed in by artificial boundaries.Grad
If you cannot run all your unit tests after each check in because you have to go to database a few hundred times, that would be impractical.Hanako
But the code has to access a database, so you have to test that functionality - using a test database created for the purpose - and that test database needs a connection string.Disjunctive
Database functionality can be tested with integration tests, not unit tests. And you can separate these from your unit tests so you run unit tests after each check in and your integration tests after each daily build or whenever you want.Hanako
Ok, so I get why you would want to seperate unit test and integration tests. So if I run my unit tests before each code checkin, how often should I run my integration tests? I oftern find that I make a change on the database at the same time as I change code.Brendabrendan
My answer would be as often as possible. "After each daily build" sounds reasonable but it may change depending on your project needs. In addition to daily builds, you may run them whenever you are uncomfortable with your changes.Hanako
Why would "having to go to the database a few hundred times" prevent you from checking in? We may just have different methods as well. I run unit tests before checking in and typically check in and build together. Then again, I'm a solo developer. Still, this seems artificially limited to me.Grad
i think that ilivewithian wants to know how to setup test project so he can properly test methods that access database...and for me it is little strange that you have to put the same web.config in test project(doesn't matter it is integration or unit test) so you could do that...what do you think?Gershom
I have to agree with Mark Brittingham here. I know the purists will say you need to mock data, but what if most of your code is iterating dataset rows returned from a database? If you have a mock object then you're just testing "imaginary" code that was written for the sole purpose of unit testing.Ultra
Testing to get a connectionstring is different from testing the database.Wongawonga
S
2

I would recommend abstracting the configuration reading part, so that it could be mocked. Something like this, see Jon_Lindeheim's reply How to read Web.Config file while Unit Test Case Debugging?

Slab answered 26/4, 2015 at 20:55 Comment(0)
L
1

If you prefer to add the web.config as a link in your test project as an explicit 'reminder' for being the configuration for your test project, you can put

copy "$(TargetDir)\Web.config" "$(TargetDir)\App.config"

in the Post-build event command line part in Visual Studio located in the project properties under the Build Events tab.

By doing so, the following will automatically be added to the .csproj file of your test proejct:

  <PropertyGroup>
    <PostBuildEvent>copy "$(TargetDir)\Web.config" "$(TargetDir)\App.config"</PostBuildEvent>
  </PropertyGroup>

For this to work, the linked file in the test project needs to have a meaningful value - i.e. Copy always in Copy to Output Directory.

Another approach, like suggested in one of the comments, would be to use the CopyToOutputDirectory directive. One might argue though, that this gives any future maintainer less clues in regards to what is happening during the build process - without having to dig through the .csproj in the test project, that is.

<ItemGroup>
    <None Include="..\MyProject\Web.config">
        <Link>$(TargetFileName).config</Link>
        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
</ItemGroup>
Liggins answered 31/1 at 12:26 Comment(1)
ah finally! This solved everything! @Stig-stavik, you're the man!Bratton

© 2022 - 2024 — McMap. All rights reserved.