Visual Studio - Unit tests loading resources in the project - c#

The goal is to run some tests given some data in those Xml files.
How would you easily load a given Xml file into an XmlDoc within the unit test methods?
Current state is:
XmlDocument doc = new XmlDocument();
string xmlFile = "4.xml";
string dir = System.IO.Directory.GetCurrentDirectory() + #"\Msgs\"
//dir is then the value of the current exe's path, which is
//d:\sourcecode\myproject\TestResults\myComputer 2009-10-08 16_07_45\Out
//we actually need:
//d:\sourcecode\myproject\Msgs\
doc.Load( dir + fileName); //should really use System.IO.Path.Combine()!
Is it just a simple matter of putting that path in an app.config? I was hoping to avoid that, given the possibility of different paths on developer machines.
Question: How would you write the algorithm to load a given Xml file into an XmlDocument in the unit test method?

There is a Visual Studio Unit Testing feature for this: DeploymentItemAttribute
I use this feature to copy all xml files in a given project folder to the unit test output folder, before testing if all required files are present.
You can use this attribute with your unit tests to copy specific files from the Project folder (or anywhere else) to the Unit Test output folder. Like so:
[TestMethod()]
[DeploymentItem("MyProjectFolder\\SomeDataFolder\\somefile.txt", "SomeOutputSubdirectory")]
public void FindResourcefile_Test()
{
string fileName = "SomeOutputSubdirectory\\somefile.txt";
Assert.IsTrue(System.IO.File.Exists(fileName));
}
You can also copy the contents of whole folders:
[TestMethod()]
[DeploymentItem("MyProjectFolder\\SomeDataFolder\\", "SomeOutputSubdirectory")]
public void FindResourcefile_Test()
{
string fileName = "SomeOutputSubdirectory\\someOtherFile.txt";
Assert.IsTrue(System.IO.File.Exists(fileName));
}
The first parameter is the source, the second the destination folder. The source is relative to your solution folder (so you can access the Unit Test project of the project being tested) and the destination is relative to the output folder of the unit test assembly.
UPDATE:
You need to enable Deployment in the Test Settings for this to work. This MSDN page explains how (it's real easy): http://msdn.microsoft.com/en-us/library/ms182475(v=vs.90).aspx#EnableDisableDeploy

You can build those files into your executable (set their "Build Action" property to "Embedded Resource") and then get them using the Assembly.GetManifestResourceStream method.

In the unit test project add a post-build event that copies the XML file to the output directory. Then, you can use your original code to get the XML file.
The post build event will look like something like this:
copy $(SolutionDir)file.xml $(ProjectDir)$(OutDir)file.xml
You may also need this to add to your path:
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)

I use a helper class to deal with getting basic paths I might want to access in my Unit Tests.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Brass9.Testing
{
public static class TestHelper
{
public static string GetBinPath()
{
return System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
}
public static string GetProjectPath()
{
string appRoot = GetBinPath();
var dir = new DirectoryInfo(appRoot).Parent.Parent.Parent;
var name = dir.Name;
return dir.FullName + #"\" + name + #"\";
}
public static string GetTestProjectPath()
{
string appRoot = GetBinPath();
var dir = new DirectoryInfo(appRoot).Parent.Parent;
return dir.FullName + #"\";
}
public static string GetMainProjectPath()
{
string testProjectPath = GetTestProjectPath();
// Just hope it ends in the standard .Tests, lop it off, done.
string path = testProjectPath.Substring(0, testProjectPath.Length - 7) + #"\";
return path;
}
}
}
Sometimes my interactions with paths are more complex; I often use a central class I name "App" to indicate some basic details about the application, like its root folder, its root namespace and module, etc. Classes will sometimes depend on App's existence, and so instead I'll place an init method on App that uses code like the above to initialize itself for test harnesses, and call that method from the Init command in a Unit Test.
(Updated)
Old Answer
I found this helps for getting arbitrary paths to access files in the project folder you intend to test (as opposed to files in the Test project folder, which can make busywork if you need to copy things over).
DirectoryInfo projectDir = new DirectoryInfo(#"..\..\..\ProjectName");
string projectDirPath = projectDir.FullName;
You can then use either of those variables to access whatever you need from the related project. Obviously swap "ProjectName" out for the actual name of your project.

Resources are just resources and that's it, no need to complicate. If you don't want to embed them then you could add these files as "Content" resources to your project and set them to Copy always. Then specify the sub-folder in your code:
var xmlDoc = XElement.Load("ProjectSubFolder\\Resource.xml");
This will automatically load the resources from the project output (running assembly location) bin\$(Configuration)\ResourceSubfolder\
This works for all types of projects, not just unit tests.

I would just put the path in the app.config and load from the default path. In my team, i am really anal about developers changing paths, so i make all my developers have the same exact paths and files on their computers, so i dont have an issue of any rogue developer changing a path to suite his workspace.
For example , all developers in my team must use C:\Project\Product\Module, etc etc. I also make sure all their software installed also is standard. This way, i can ghost any machine into any other easily.

I think in VS.NET 2012 DeploymentItem attribute works without any Test Settings configuration.

Related

Two projects in a solution that use different default paths

I have two projects in my solution, ChatProject and ChatProjectTest.
ChatProjectTest has a single class file that has many methods that test methods in ChatProject.
My problem is that ChatProject uses some files, like data.bin and log.txt, that are stored in the .exe folder (that is ChatProject\bin\Debug\ChatProject.exe). I want ChatProjectTest to use the same files, and not go ahead and create or load new files in its own .exe folder (ChatProjectTest\bin\Debug\ChatProject.exe), like it does currently.
To be clear, the file paths are stored in constant variables in ChatProject like this:
private static string DATABASE_NAME = "data.bin";
I didn't hardcode their paths.
One solution can be configuring your projects to put all the output files into the same the same bin/ directory under your solution root (instead of two separate bin/ folders).
To do this, go to the "Build" tab under project settings and set "Output path" to
$(SolutionDir)Bin\$(Configuration)\
Repeat this for both of your projects.
If you don't specify a path to a file it looks in the current directory for them.
To use the same ones, you either have to specify some sort of relative path from the Test Output, or build the tests into the same folder as the Project.
You may allow the target file path to be injected e. g. via an optional constructor parameter:
public class SampleClass
{
private readonly string OutputDirectory;
public SampleClass(string outputDirectory = ".")
{
OutputDirectory = outputDirectory;
}
public void WriteData(string data)
{
File.AppendAllText($"{OutputDirectory}\\data.bin", data);
}
}
[TestClass]
public class SampleClassTest
{
[TestMethod]
public void WriteDataTest()
{
var sut = new SampleClass("..\\..\\..\\ChatProject\\bin\\Debug");
sut.WriteData("data");
}
}

Referencing a folder in my project with a SpecFlow test

I'm trying to write a SpecFlow test where I test what happens when my application reads a certain structure of folders and files. I want to include these folders and files in my project so the tests don't just run on my own computer.
For example, I have two folders in my Specs project. One called 'SimpleTestModel' and the other called 'ComplexTestModel'. How can I reference these folders in my SpecFlow tests?
You want a Test Fixture.
From Wikipedia:
In software testing, a test fixture is a fixed state of the software under test used as a baseline for running tests; also known as the test context. It may also refer to the actions performed in order to bring the system into such a state.
Examples of fixtures:
Loading a database with a specific, known set of data
Erasing a hard disk and installing a known clean operating system installation
Copying a specific known set of files
Preparation of input data and set-up/creation of fake or mock objects
Software used to systematically run reproducible tests on a piece of software under test is known as a test harness; part of its job is to set up suitable test fixtures.
For your specific problem:
Create a Fixtures directory in your SpecFlow test project. Inside that create any numbers of sub directories based on your tests to set up the directory and file structures that you need.
Add an <appSettings> in App.config entry define the root folder for all your test fixtures
<configuration>
...
<appSettings>
<!-- Path relative to the build output directory -->
<add name="FixturesRootDirectory" value="..\..\Fixtures" />
</appSettings>
...
</configuration>
In a [BeforeScenario] hook, set the absolute path to the fixtures directory on the current scenario context (reference: How do I get the path of the assembly the code is in?)
using System.Configuration;
using System.IO;
using TechTalk.SpecFlow;
namespace Foo
{
[Binding]
public class CommonHooks
{
[BeforeScenario]
public void BeforeScenario()
{
InitFixturesPath();
}
private void InitFixturesPath()
{
if (ScenarioContext.Current.ContainsKey("FixturesPath"))
return;
string codeBase = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase)
+ Path.DirectorySeparatorChar
+ ConfigurationManager.AppSettings["FixturesRootDirectory"];
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
ScenarioContext.Current.Set<string>("FixturesPath", Path.GetDirectoryName(path));
}
}
}
Now you can use ScenarioContext.Current.Get<string>("FixturesPath") to get the root directory for all of your fixtures. You could even write your own Fixtures helper class:
public static class FixturesHelper
{
public static string Path { get; set; }
// other methods and properties making it easier to use fixtures
}

Get relative path of XML File located in another project but Same Solution

I have found many Articles and Questions about finding an XML file but all of them are for Same Project.
Suppose I have my Project structure as shown below:
ABC <------ Solution
|--ABC.Data <------ Project
| |--XMLFiles <-------Folder
| |--AA.xml
|
|--ABC.Client <------ Project
| |--ViewModels <-------Folder
| |-MainViewModel.cs
Now I would like to load AA.xml file in MainViewModel.cs using XDocument.Load(....path.....).
So, how can I get path?
You will have to move back from the executing assembly folder on parent of ABC.Data and ABC.Client. For example the executing assembly is located in ABC.Client->ViewModels->Bin->Debug
XDocument.Load("../../../../../ABC.Data/ABC.Data/AA.xml");
But practically I see no use of it as the project hierarchy wont be there when you deploy the application. If you have XML file that need to be accessed by multiple assemblies the simply put that xml file in Execution folder. All the assemblies will be able to access it.
You can also make a separate assembly that exposes the AA.xml file data for read and write for all other projects by adding reference of that assembly to those projects. I would prefer this method.
you need a couple of settings to make your scenario work
first right click the xml file(or any file you want to access) and set 'Copy to Output Directory' to 'Copy Always' and when you build or publish the folder structure of that file(AA.xml in your case) will be created on bin or publish(web project) folder and you have simply have to write this code
var path = HostingEnvironment.ApplicationPhysicalPath+"\\bin";//path to you bin
if you are using winforms
var path = Application.StartupPath;//path to you bin
finally
var fullpath=path+"\\XMLFiles\\AA.xml";
XDocument.Load(fullpath) ;
however the better approach will be to create a Helper Class that will fetch the xml content for you application such as
public static class XMLHelpers
{
public static XMLDocument GetXML(string KEY)
{
string file="";
switch(KEY)
{
case "AA": file=Path.Combine(file,"AA.txt");
break;
}
var xmldoc=new XMLDocument();
xmldoc.Load(file);
return xmldoc;
}
}

Get the path from a SpecFlow feature file in a Step Definition

Is it possible to retrieve the path of a SpecFlow feature file during runtime in a Step Definition?
Snippet:
[Given(#"Some given statement")]
public void GivenSomeGivenStatement() {
var featureFilePath = // retrieve the path of the feature file
// that executes this step.
}
Context:
We do testing on databases and queries. The source data is created in Excel files and .SQL files (for check queries). These source data are large datasets, not feasible to put into the feature files itself or use the SpecFlow.Plus.Excel extension.
To keep the data close to the feature file, we want to have this data in the same folder as the feature file itself. To achieve this, we need the path to this feature file, so we also have the path to the testdata.
Here's a suggestion. This is just something I put together quickly so lots of room to improve. It relies on the Feature file name being identical to the title of the feature you provide in the description. It also assumes you have a conventional folder structure for your SpecFlow VS project as there is a lot of string manipulation.
Firstly, the calling code should use the SpecFlow BeforeScenario attribute. Something like this:
public void BeforeScenario()
{
//grabs Feature Title from SpecFlow context
var featureName = FeatureContext.Current.FeatureInfo.Title;
//Calls method to obtain path of file
var featureFilePath = GetFeatureFilePath(featureName);
}
The method GetFeatureFilePath will then look like this:
private static string GetFeatureFilePath(string featureName)
{
string startupPath = Environment.CurrentDirectory;
var splitStartupPath = startupPath.Split(new[] {"\\"}, StringSplitOptions.None);
var featureFolder = splitStartupPath[0] + #"\\" +
splitStartupPath[1] + #"\\" +
splitStartupPath[2] + #"\\" +
splitStartupPath[3] + #"\\" +
splitStartupPath[4] + #"\\" +
splitStartupPath[5] + #"\\Features\";
var dir = new DirectoryInfo(featureFolder);
foreach (var fi in dir.GetFiles())
{
if (fi.FullName.Contains(featureName))
return fi.FullName;
}
return "No Feature File Found With Title: " + featureName;
}
It grabs your current directory and splits it to the point where the Features folder should be. It then iterates through each feature file until it finds one that contains your feature title in its path name and returns that as a full path.
I'm not aware of any other way to get this currently.
I don't think knowing the path to the feature file will be possible, as the feature file is used to generate a file containing the unit tests and this is compiled and copied to the test run directory.
The simplest thing will be to set the files as part of the solution and then have them copied to the output directory when the project builds.
If you are using NUnit as the test framework then the files should be in the same directory as the tests are executing so you should just be able to load them without specifying any path, or using the Assembly.GetExecutingAssembly().Location to findout where the code is actually executing.
If you are using MSTest then you need to add a [DeploymentItem(FileToDeploy)] attribute to the test to ensure that the file actually gets deployed with the tests when they are run. Unfortunately as Specflow generates the tests it won't add this for you. To solve this you need to create a partial class which has the same name as the class which contains the tests. This class is called the same as the feature with 'Feature' tagged on the end. So if you have this in your feature:
Feature: Do A Thing
The your test class will be called DoAThingFeature
so you need to create a partial class like this:
[DeploymentItem("FileToDeploy.ext")]
public partial class DoAThingFeature
{}
to ensure that MsTest copies the file you need to the correct directory.
Edit
based on your comment you could maybe do something similar to this
add tags to your feature #hasFiles #source:myFile.xlsx
Then you could add this class:
[Binding]
public class DeployFiles
{
[BeforeScenario("hasFiles")]
public void CopyFiles()
{
..in here find the current executing directory and search
..the subtree for any files defined in the
..ScenarioInfo.Tags array that start with `source:` and copy
..them to the current executing directory
}
}
then any scenario tagged with the #hasFiles will deploy any files specified by #source tags to the root directory where the tests are running.
Not pretty and I'm not certain it'll work, but it might.
maybe this could help you , in .net 4.5 you can get the hold of the path to the caller, take a look at this thread source path in .net 4.5

What does `DeploymentItem` attribute mean?

Let's say we have a short program:
namespace ConsoleTryIt
{
static class Program
{
static void Main(string[] args)
{
var sum = Add(1, 2);
}
private static int Add(int p, int p2)
{
return p + p2;
}
}
}
When create the unit test class for this class, Visual Studio create a test method with the attribute DeploymentItem. I read MSDN about this attribute but still don't get what it means.
/// <summary>
///A test for Add
///</summary>
[TestMethod()]
[DeploymentItem("ConsoleTryIt.exe")]
public void AddTest()
{
var expected = 122;
var actual = Program_Accessor.Add(1, 121);
Assert.AreEqual(expected, actual);
}
If you get the idea, please share!
Edit
Thanks everyone for your answers. So the idea is to copy the item given in the argument to the testing evironment's folder. My next question is: why does this method need this attribute while others don't?
I guess it's related to the private members on the tested class but nothing clear to me.
Please continue to discuss.
This specifies files that are required by the specific test. The test system creates a new directory where the tests are run from. With this attribute, you can make the test system copy specific files to that new directory.
Is used deploy files that are not necessary present in the Output directory to the folder used for that particular TestRun.
in the example you posted above, the test environment makes sure that "consoleTryIt.exe" is copied(and therefore present) in the test folder.
If the file is not found, the test is not even run, and a FileNotFound Exception is returned.
This means that the item is copied to the 'TestResults\Out' folder and is basically an artifact/necessary item for the test run. Thus, it is stored apart from the bin directory and does not get overwritten.
This is especially useful when running the tests in different environments (build server, no hard-coded paths...) and of course it is necessary to make the tests repeatable.
HTH.
Thomas
Ensures the files required for the test are copied over to the folder where MSTest runs its tests TestResults\Out.
Files in your solution should have "Copy Always" set for the files to be first copied to bin folder and then to MSTest's folder.
Make sure you have "Enable deployment" checked in testrunconfig.

Categories

Resources