I am creating a c# class (Class.cs) that needs to look for a file(FileINeedToFind.cs) by name from a local directory. The directory is in a different directory from the class file.
folder structure:
Root
-> ClassFolder
--> Class.cs
-> FileFolder
--> FileINeedToFind.cs
When I run a test for the class, it always comes back with the test results path.
How can I get the class to get its own local path, and not that of the test results assembly?
When a program runs, you should assume that an object has no awareness of the source code that generated it. The code is compiled into an assembly, and that assembly is loaded when you run the program, so the "current" directory by default is the location of the executing assembly.
In short, this is not something you can cleanly do. It's best if your code does not rely on the concept of source-relative directories. It may help you to achieve your goals if you change the build action for the file you're looking for so it's copied to the output directory. Then that file's relative path at runtime should mirror the relative path within your project folder.
To get the path to the executing assembly (the "current" path may be changed by any other executing code, so it's best to not rely on that), use this code:
System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().CodeBase);
In MSTest, tests are (by default) executed in a different folder than the folder which contains your code, via test deployment. You can configure what gets deployed, but I tend to agree with #Jacob that this isn't something you typically want to do.
However, I differ with #Jacob about how to get the current directory in a test. In MSTest, the right way to do this is with TestContext.DeploymentDirectory.
Related
I am using a 3rd party nuget package for puppeteer. I cant get any code involving this library to work. I looked into the code and found the following line of code
DownloadsFolder = Path.Combine(Directory.GetCurrentDirectory(), ".local-chromium");
The issue is that when I run a unit test Directory.GetCurrentDirectory()returns Windows/system32 and the code fails because it doesn't have permission to write to this directory.
It would seem that this is intended behavior https://github.com/nunit/nunit/issues/1768 . The issue here is that I can't modify the source code of this dll so I am stuck. Is there any way to modify nunit so that it returns a directory that I can define for Directory.GetCurrentDirectory()?
If I understand correctly, the code that calls Directory.GetCurrentDirectory is in a library used by the application you are testing. (If it were in your own NUnit tests, then this answer would have to be different.) This is a bad design choice for library code, since it assumes that the application calling the library has set (or left) the current directory to the appropriate location. (IMO, it's OK for an application to do this, but not a library.)
I'm guessing that you are running under Visual Studio, since the current directory ends up being System32.
By design, NUnit itself never changes the current directory, so it is left just as it was when the program was first run. You can change it in your tests, but there are risks in doing so.
That explains why you see the problem. Here is a workaround.
If you don't care (for test purposes) where the folder is located, create a temp folder in your test setup and remove it in teardown. You can do this for each test (SetUp and TearDown) or for the entire fixture (OneTimeSetUp and OneTimeTearDown).
In the same setup location, set the current directory to that temporary folder, saving the original current directory. Restore it in the appropriate teardown method.
Ensure that no tests using the directory can run in parallel. If you don't use the ParallelizableAttribute at all you should be OK. But if you have set it to some value at the assembly or other higher level, mark the classes that contain these tests as [NonParallelizable].
The last step is very important. The current directory is set for the entire process, so it would affect all executing tests. It's important that no other tests run while the changed directory is in effect.
I am loading this file which is located in a folder i created, the same folder is also rendered in the bin/debug folder, but i need to locate the file in the folder i created, the thing is the method which should find the path of the file by its name gives me the path of the debug folder instead of the on i created.
I have harcoded the path to show you the real location.
var path2 = System.IO.Path.Combine(Environment.CurrentDirectory, "xulrunner");
var hardcodedPath= "C:\\Users\\Alan\\Desktop\\TabControl\\TabControl\\TabControl\\TabControl\\xulrunner";
This is the result of path2 variable:
C:\\Users\\Alan\\Desktop\\TabControl\\TabControl\\TabControl\\TabControl\\bin\\Debug\\xulrunner"
As you can see its pointing to different folder as hardcoded path.
When running from Visual Studio, the working directory is different from your project location, usually it is two folders up (from bin\Debug\). In your distributed application, the working directory might be the same as that of other files you want to access since you copy some other files to the working directory.
I would recommend to keep your code the same, and copy the files you require from your project to the output directory. You can do that by simply setting the Copy to Output Directory setting to Copy always or Copy if newer.
Changing the path to go two folders up is a bad idea, since this won't be true in production scenarios, and will thus break your code.
To get exactly what you are looking for, you could do the following:
var path2 = System.IO.Path.GetFullPath(System.IO.Path.Combine(Environment.CurrentDirectory, "..\\..\\xulrunner"));
However, please be aware that Environment.CurrentDirectory can change as your program runs. I have found that using the %PROGRAMDATA% environment variable works well for creating a location where you save data. It allows for a more consistent solution for using paths.
Additionally, the code snippet provided will only really be useful when you are running through the IDE's debugger. When you deploy, it will likely not give you what you are expecting.
It would help to know what is the overall functional result you are hoping for.
I have two projects .NET Web API 2 projects, MyProject and MyProject.Tests. MyProject has a settings.json file which I use to store database credentials. I used a custom external settings file because the database credentials includes a username and password that shouldn't be bundled in with other settings and are user-specific.
I wrote a ConfigManager() class with a GetDbConnectionString() method that reads the DB credentials from an external file and generates a connection string. It uses a relative path to the config file that lives in the project directory. This works fine during normal execution, because the path is relative to MyProject. However when running units tests, all relative paths are relative to MyProject.Tests, and fail to find the proper file. I can use an absolute path, but I would prefer for all settings files to be bundled in the project directory.
Is there a way I can have the unit tests fine the settings file properly without duplicating it in the unit test project and without using absolute paths>
You may want to use Directory.GetCurrentDirectory() or System.Reflection.Assembly.GetExecutingAssembly().Location to get to the path.
Directory.GetCurrentDirectory() will give you the current folder (which is more of a context, as you can create a shortcut and pass a completely different folder as your current directory than the one where your exe is present).
System.Reflection.Assembly.GetExecutingAssembly().Location will give you the path of the dll (or exe depending on compilation option).
System.Reflection.Assembly.GetAssembly(typeof(ConfigManager)).Location; should give what you are looking for.
Refer to suggestions in
How do I get the path of the assembly the code is in?
A C# executable can reside in 3 different locations during it's life cycle when going from development to end user distribution:
{project directory}\bin\Debug
{project directory}\bin\Release
{end user install directory}
My app has a web page and some ancillary files that I like to put in a sub-directory called \video, right beneath the directory that contains the executable. What I really don't want to do is copy the sub-directory around between the 3 directories listed above. In other words, obviously I don't want to end up with:
{project directory}\bin\Debug\video
{project directory}\bin\Release\video
{end user install directory}\video
with the inherent re-copying every time the file in \video change.
What is a convenient overall strategy for keeping data files distributed with an application in a centralized directory, a directory that will be added to the setup program when the application is distributed? I'm hoping that I don't have to add build tasks to copy over the data files to the \Debug and \Release sub-directories every time the application is run in the Visual Studio 2012 IDE. That leads to potential errors if I forget to copy a file during the build tasks and can create a mess if there a lot of files, especially large ones.
Is there a way to build the top level data path that detects conveniently each of the 3 different runtime contexts? I don't mind if I have to wrap all relative data paths with a method that canonicalizes the data path when a relative path is passed to another method. Here's an example using a fictitious method named fixRelPath() that would expand a relative path properly before passing it to a sample method named openFile():
openFile(fixRelPath(".\\video\\temp.html"));
You should put your files in the AppData directory.
Per User:
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
Shared:
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
So... I used to think that when you accessed a file but specified the name without a path (CAISLog.csv in my case) that .NET would expect the file to reside at the same path as the running .exe.
This works when I'm stepping through a solution (C# .NET2.* VS2K5) but when I run the app in normal mode (Started by a Websphere MQ Trigger monitor & running in the background as a network service) instead of accessing the file at the path where the .exe is it's being looked for at C:\WINDOWS\system32. If it matters The parent task's .exe is in almost the same folder structure/path as my app
I get a matching error: "System.UnauthorizedAccessException: Access to the path 'C:\WINDOWS\system32\CAISLog.csv' is denied."
My workaround is to just fully qualify the location of my file. What I want to understand, however is "What is the .NET rule that governs how a path is resolved when only the file name is specified during IO?" I feel I'm missing some basic concept and it's bugging me bad.
edit - I'm not sure it's a.NET rule per se but Schmuli seems to be explaining the concept a little clearer. I will definitely try Rob Prouse's suggestions in the future so +1 on that too.
If anyone has some re-wording suggestions that emphasize I don't really care about finding the path to my .exe - rather just didn't understand what was going on with relative path resolution (and I may still have my terminlogy screwed up)...
When an application (WinForms) starts up, the Environment.CurrentDirectory contains the path to the application folder (i.e. the folder that contains the .exe assembly). Using any of the File Dialogs, ex. OpenFileDialog, SaveFileDialog, etc. will cause the current directory to change (if a different folder was selected).
When running a Windows Service, its containing folder is C:\Windows\System32, as that is the System folder and it is the System (i.e. the Operation System) that is actually running your Windows Service.
Note that specifying a relative path in most of the System.IO objects, will fall back to using the Environment.CurrentDirectory property.
As mentioned, there are several ways to obtain the path of the service executable, using Assembly.GetEntryAssembly() or Assembly.GetExecutingAssembly() and then using either the Location property or the CodeBase property (be aware that this is the file path, not directory, of the executable).
Another option is to use:
`System.IO.Directory.SetCurrentDirectory( System.AppDomain.CurrentDomain.BaseDirectory );`
Make the call in the Service's OnStart method, applying it to the whole application.
It is based on the current working directory which may or may not be the same as where your application resides, especially if started from a different program or a shortcut with a different working directory.
Rather than hard code the path, get the path to your program and use it. You can do this with something like this
Assembly ass = Assembly.GetEntryAssembly();
string dir = Path.GetDirectoryName(ass.Location);
string filename = Path.Combine( dir, "CAISLog.csv" );
This assumes that the entry assembly is where your file is. If not, you can change up getting the assembly for something like;
Assembly ass = Assembly.GetAssembly( typeof( AClassInYourAssembly ) );
Relative Path resolution never works against the path of the launching executable. It always works against the process' Current Directory, and you can't really expect that to always be set to the directory the .exe lives in.
If you need that behavior, then take care to find out the right path on your own and provide a fully qualified path to the file operations.
You can use this to specify a path that resides at the same path of your exe #"..\CAISLog.csv". Please note that the double dots refer to the parent directory of where ever your .exe lies.
RWendi