Load file during Unit Test in Unity3d's Unity Test Runner - c#

Using Unity 2018.1.1f1 and Visual Studio Community 2017
I've been creating unit tests using unity's built in Test Runner. In this particular unit test, I want to read a file's content into a string. Without having to build a static path to the file (which will sit next to the unit test class itself), how can I reference it?
The folder structure looks like this:
Directory:
Assets/Plugins/MyTools/Common/Properties/_tests/
Files:
MyTools.Common.Properties.Tests.asmdef
PropertiesTests.cs
test.txt
test-prop-file.txt
The test and some output details:
namespace MyTools.Common.Properties.Tests {
public class PropertiesTests {
[Test]
public void ParsePropsFile() {
Debug.Log(Assembly.GetExecutingAssembly().GetName());
// MyTools.Common.Properties.Tests, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
Debug.Log(GetType().Assembly.GetManifestResourceNames().Length);
// 0
Debug.Log(Assembly.GetExecutingAssembly().GetManifestResourceNames().Length);
// 0
}
}
}
In visual studio, I've tried to set the properties (Build Action = Embedded Resource, and Copy to Output Directory = Copy Always) of test-prop-file.txt and test.txt but they do not seem to do anything. Also, when I close VS and reopen it, these are set back to what they were before (None and Do Not Copy). I've also tried other combos like Build Action = Resource or Content but still nothing.
So how can I go about loading these files in the unit test without referencing them directly with a static path like D:/Code/Unity/MyProject/Assets/Plugins/MyTools/Common/Properties/_tests/test.txt?

The location of your current test assembly can be retrieved with:
TestContext.CurrentContext.TestDirectory
(If I've remembered correctly - the middle 'CurrentContext' may not be necessary!)

Related

Location of original assembly(not the current executing)

Suppose I have a solution which contains 4 projects, A, A_UnitTests, B, and B_UnitTests.
Project A has a data file, which is added as a link to A_UnitTests and set to copy to the output directory. When unit tests are run or when the code is executed in production, the path to that file is correctly identified using the following code snippet:
public static string GetFullPath(string relativePath)
{
string retVal = string.Empty;
if (System.Web.HttpContext.Current == null)
{
string locationBeforeShadowCopy = typeof(A.SomeClassInA).Assembly.CodeBase;
UriBuilder uri = new UriBuilder(locationBeforeShadowCopy);
string locationWithoutUriPrefixes = Uri.UnescapeDataString(uri.Path);
string dir = Path.GetDirectoryName(locationWithoutUriPrefixes);
retVal = Path.Combine(dir, relativePath);
}
else
{
// stuff that doesn't matter
}
return retVal;
}
However, I have a new testcase in B_UnitTests which attempts to use this code path to find the file location. However, even though I call typeof(A.SomeClassInA).Assembly.CodeBase, it is being called from B_UnitTests, using its referenced DLLs. This means the path return is the B_UnitTests output directory + a relative path. So it doesn't find the data file.
Without resorting to hard coded settings and build scripts, what could I use to specify the correct path?
Update (clarification)
The real issue is with typeForClassInA.Assembly.CodeBase returning the path of the executing assembly rather than A itself. It seems very wrong to provide a type that comes from some assembly but instead of returning the original assembly location, it returns the path to the executing assembly which happens to have a reference to it.
If there is a reference to 'typeForClassInA', then its assembly will be being copied into the output directory of B_UnitTests. So when you ask for CodeBase of that type's assembly from a test in B_UnitTests, it is (correctly) pointing at the version of assembly A in the B_UnitTests output folder, because that's where it's being loaded from.
I admit that I avoid using Shadow Copy to avoid exactly these kinds of problems of locating resources which are alongside the assembly, since ShadowCopy doesn't understand that they are needed, and they don't get shadow copied.
Another thing which can help is to build all the projects into the same output folder by changing all the project output folders to be "..\bin". For example, this would mean that A_UnitTests would not need the link to the resource file (once shadow copy is off).
I have a method similar to the one you've shown which goes "up" from the assembly's location (which for me is the shared bin folder) to the solution's location; and my relative paths are 'rooted' at that folder.
If that all sounds too complex, you could just use the same approach that A_UnitTests did, of including a link to it from B_UnitTests.

c# Register custom dll as processor to Printer++ Config file

I am trying to create a custom printer driver to generate images. For this, I have installed Printer++ which converts print files to postscripts. To convert postscript file to image, I am using ghostscript. Independently both the processes are running fine and I am able to achieve what is required.
But, I need a custom process to generate images in one go. I followed through the Printer++ tutorial but it didn't work.
This is what I have done:
I installed Printer++ and gave the name of the printer driver as- Septane.
In Visual Studio, I created a project- Test.
And the following code in Processor.cs class:
using System;
using System.Net.Mail;
using PrinterPlusPlusSDK;
namespace Test
{
public class Processor : PrinterPlusPlusSDK.IProcessor
{
public PrinterPlusPlusSDK.ProcessResult Process(string key, string psFilename)
{
//Convert PS to Png
psFilename = "b.ps";
MessageBox.Show("Rahul");
ConvertPsToTxt(psFilename);
}
public static string ConvertPsToTxt(string psFilename, string txtFilename)
{
var retVal = string.Empty;
var errorMessage = string.Empty;
var command = "C:\\PrinterPlusPlus\\gs\\gswin64.exe";
var args = string.Format("-dNOPAUSE -dBATCH -dFirstPage=1 -q -r300 -sDEVICE=png256 -sOutputFile=", psFilename, txtFilename);
retVal = Shell.ExecuteShellCommand(command, args, ref errorMessage);
return retVal;
}
}
}
This class inherits from PrinterPlusPlusSDK.IProcessor and implements the PrinterPlusPlusSDK.ProcessResult Process function. I have tested the standalone console project (without using PrinterPlusPlusSDK processor) and that converts ps to png successfully.
Now, as per the tutorial, the dll needs to be deployed to printer++ and registered as a processor. I copied Test.dll to Printer++ installation folder and added an entry to PrinterPlusPlus.exe.config file.
The config entry looks like:
<processors>
<add key="Test"
value="Test.Processor, Septane, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</processors>
That's it. Now, when I print a file, it gives an error:
Proccessor not found for: Septane
What am I doing wrong?
If anyone has better idea for achieving the same, please let me know. Actually, mine is a commercial product so can't use CutePDF/VerPDf kind options.
Edit: I now know why I was getting error- Processor not found. I renamed my printer to Test and the error disappeared. I have edited my code as well.
For testing, I have added a message box. I expected to get a popup once I give print command. But that is not the case. The ps file is getting generated without an error and that's it. I can't view pop-up message and there is no png converted file. Can someone please help me resolve this issue at least? It doesn't seem to be picking up the dll at all.
Thanks.
Remember The Printer name must be same as the you dll name
If your printer driver name is Septane. Then you must have to create a project name with "Septane". In that case project name "Test" will not work.

Cannot Use ConfigurationManager inside Unit Test Project

I'm trying to write a unit test for my project, but it will not let me use the Configuration Manager. Right now my project is set up like
ASP.Net application (all aspx pages)
ProjectCore (all C# files - model)
ProjectTest (all tests)
in my ProjectCore, I am able to access the ConfigurationManager object from System.Configuration and pass information onto the project. However, when I ran a test where the ConfigurationManager is involved, I get the error
System.NullReferenceException: Object reference not set to an instance of an object.
Here is an example of the test
using System.Configuration;
[TestMethod]
public void TestDatabaseExists()
{
//Error when I declare ConfigurationManager
Assert.IsNotNull(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString
}
in my other tests, ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString is what I set my data adapter's configuration string to, and returns a null error on the tests but not when I actually use the website. Any ideas?
It could be one of several issues:
You didn't add app.config to your ProjectTest project.
You didn't add connection string in your app.config.
You are doing a unit test and in unit test your concentration should be the particular method trying to test and should remove extraneous dependencies. in this case, try mocking/moleing(use Microsoft Mole and Pex) system.configuration class; that will give a solution for sure.
What I am saying, once you install MS moles-and-pex -> in your test project solution -> right-click the system assembly and choose create mole.
That will give you a mole'ed version of configuration class which in turn will have a mocked version of configuration class -- using which you can bypass the problem you are facing.
You also can use special configuration paths with the ExeConfigurationFileMap:
// Get the machine.config file.
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
// You may want to map to your own exe.config file here.
fileMap.ExeConfigFilename = #"C:\test\ConfigurationManager.exe.config";
// You can add here LocalUserConfigFilename, MachineConfigFilename and RoamingUserConfigFilename, too
System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
It is related to the /noisolation parameter in the command line of mstest.exe.
Omitting the /noisolation parameter, it works.
first of all you must make sure that you have an app.config file in your nunit tests project.
To add it, you can open the project properties (right click on the project)
Enter the details of your connection, it will generate a app.config file or add the right section within :
In your Test class, add the reference to : System.Configuration;
=> using System.Configuration;
For example you could use your connectionString by this way :
[TestFixture]
public class CommandesDALUnitTest
{
private string _connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
[Test]
public void Method_Test()
{
string test = _connectionString;
....
}
}

How to find path to content in a different project

I'm trying to write a test that actually has to use an executable that sits in output directory of another project, I referenced that "other project" in my test, now what? How do you access a file in output directory of "other project" ?
File has BuildAction = Content and Copy to output directory = Copy Allways
System.Reflection.Assembly methods aren't helping
Easiest way is to go to your "other" project's method you want to call and select "Create Unit Tests...".
Let it create the stub for you. You should get a chunk of code like below and it should add the reference and create a Test References folder with an YourApplication.accessor file in it.
/// <summary>
/// Your test
///</summary>
[TestMethod()]
[DeploymentItem("YourApplication.exe")]
public void YourTest()
{
//your entry point that you want to call in the exe
Program_Accessor.YourMethod();
//Your assert test here
}
Copy the DeploymentItem attribute to your test, wire up your method call, and try running the test.

Data Driven MSTest: DataRow is always null

I am having a problem using Visual Studio data driven testing. I have tried to deconstruct this to the simplest example.
I am using Visual Studio 2012. I create a new unit test project.
I am referencing system data.
My code looks like this:
namespace UnitTestProject1
{
[TestClass]
public class UnitTest1
{
[DeploymentItem(#"OrderService.csv")]
[DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", "OrderService.csv", "OrderService#csv", DataAccessMethod.Sequential)]
[TestMethod]
public void TestMethod1()
{
try
{
Debug.WriteLine(TestContext.DataRow["ID"]);
}
catch (Exception ex)
{
Assert.Fail();
}
}
public TestContext TestContext { get; set; }
}
}
I have a very small csv file that I have set the Build Options to to 'Content' and 'Copy Always'. I have added a .testsettings file to the solution, and set enable deployment, and added the csv file.
I have tried this with and without |DataDirectory|, and with/without a full path specified (the same path that I get with Environment.CurrentDirectory). I've tried variations of "../" and "../../" just in case. Right now the csv is at the project root level, same as the .cs test code file.
I have tried variations with xml as well as csv.
TestContext is not null, but DataRow always is.
I have not gotten this to work despite a lot of fiddling with it. I'm not sure what I'm doing wrong.
Does mstest create a log anywhere that would tell me if it is failing to find the csv file, or what specific error might be causing DataRow to fail to populate?
I have tried the following csv files:
ID
1
2
3
4
and
ID, Whatever
1,0
2,1
3,2
4,3
So far, no dice.
I am using ReSharper, could it be interfering in some way?
Updated
I have it mostly working now! I am able to use XML, but when I use CSV my column, which is named ID comes back as ID
Not sure why. I've checked the actual file of course, and no weird characters are present.
For anyone having a similar problem, I turned off Just My Code and enabled Net Framework source stepping, etc. so that I could get more detailed debug information. This allowed me to determine that ReSharper was causing me problems. I disabled resharper and modified my attributes like this:
[DeploymentItem("UnitTestProject1\\OrderService.csv")]
[DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", "|DataDirectory|\\bin\\Debug\\OrderService.csv", "OrderService#csv", DataAccessMethod.Sequential)]
And it worked (except as noted). I am still suspicious of the "bin\debug" in my path, but I'm just happy my DataRow is no longer null.
Thanks!
Any ideas?
I was struggling with a similar problem today when trying to make data-driven tests work with CSV input file. The name of the first column had some garbage at the beggining of it, i.e. ID instead of just ID.
It turned out it was an encoding issue. The CSV file was saved in UTF-8 which adds a byte order mark at the beginning, obviously confusing the parser. Once I saved the file in ANSI encoding, it worked as expected.
I know it's an old question, but this information might help someone else ending up on this page.
Have you tried adding it through the properties window?
Go to Test menu -> Windows -> Test View -> the tests will load up.
Click on the test to alter i.e. TestMethod1 and press F4 (properties).
Look for 'Data Source' and click the ellipses next to it
It will walk you through a wizard that sets up the attributes properly for the TestMethod
You have the deployment part set up properly, which is normally the big stumbling block.
You also don't have to set the build action to Copy Always as the deployment does this for you. This option is used if you include items like .xml files you use for configs, or icons/images as part of your project.
Update 1:
Also try this tutorial on MSDN.
Update 2:
Try this post, involving ProcMon
I see that you said you tried putting the CSV itself into the testsettings file, but have you tried just putting in the directory?
<Deployment>
<DeploymentItem filename="Test\Data\" />
</Deployment>
Then your DataSource line will look something like this:
[DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", "|DataDirectory|\\YOURCSV.csv", "YOURCSV#csv", DataAccessMethod.Sequential)]
If you do it this way, you don't need to specify the DeploymentItem line.
Our folder structure looks like this: Trunk\Test\Test\Data
We include: Test\Data in the deployment
We then access Test\Data via the |DataDirectory|\
All CSVs live within the \Data folder

Categories