Saturday, June 29, 2013

How to calculate real path of LocalDb MDF files in your integration tests when your MDF file is located under your main project?

This is a short post to describe a common issue in Visual Studio 2012 usinga  LocalDb MDF files. Usually if you don't use normal instances of SQL Server or SQL Express and instead use a LocalDb database in VS2012 in your web project, it is located in a folder named "App_Data". In this case your connection string usually use a predefined keyword called |DataDirectory| to locate the mdf file:
<connectionStrings>
<add name="MyDbContext" 
connectionString="metadata=res://*/MyDataModel.csdl|res://*/MyDataModel.ssdl|res://*/MyDataModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=(LocalDb)\v11.0;attachdbfilename=|DataDirectory|\Mydb.mdf;initial catalog=Mydb;integrated
 security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
</connectionStrings>

Unfortunately when you run your integration test projects, |DataDirectory| expression in runtime maps to a non-existing folder under the test project and not your main project! Also trying to replace it with some relative addresses to point to the root folder of your solution and then pointing to the main project\App_Data will not succeed!

I used the following solution in my test projects. What you need is just adding an AssemblyInit method to one of your test classes and setting the DataDirectory of your assembly to the actual App_Data folder in main project. In the following code replace YourProjectMainFolder with the name of the folder containing the real App_Data in your main project:

using System;
using System.Threading;
using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class HomeControllerTest
{

   [AssemblyInitialize]
   public static void AssemblyInit(TestContext context)
   {

        var asmPath = Thread.GetDomain().BaseDirectory;

        var parent1 = Directory.GetParent(asmPath).ToString();
        var parent2 = Directory.GetParent(parent1).ToString();
        var parent3 = Directory.GetParent(parent2).ToString();
        var mdfPath = Path.Combine(parent3, "YourProjectMainFolder ", "App_Data");

        AppDomain.CurrentDomain.SetData("DataDirectory", mdfPath);
  }


  [TestMethod]
  public void AnyTestMethod()
  {
     ...
  }

   ...
}

The above code simply run AssemblyInit before any other test, It calculates the folder path that exactly point to your mdf file and put the calculated address in DataDirectory variable which is directly used as the value of |DataDirectory| keyword in your connection string. 

Important note: Usually you need to add an App.config file to your integration test project and put the same connection string of your main project. This is because when integration tests start to work they call main controllers or services and they will run under your test project assembly context not your main project:

<?xml version="1.0" encoding="utf-8" ?>
<!--
Note: Add entries to the App.config file for configuration settings
that apply only to the Test project.
-->
<configuration>
<appSettings>

</appSettings>

<connectionStrings>
<add name="MyDbContext"     
connectionString="metadata=res://*/MyDataModel.csdl|res://*/MyDataModel.ssdl|res://*/MyDataModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=(LocalDb)\v11.0;attachdbfilename=|DataDirectory|\Mydb.mdf;initial catalog=Mydb;integrated 
security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
</connectionStrings>
</configuration>


No comments:

Post a Comment