Teamcity - writing tests that access a separate JSON file - c#

I have TeamCity running for a C# project. The Unit tests are written using MSTest and they include an external JSON file. They are loaded in because they're large and I don't want to have to escape them in C#.
I import them like this:
[TestInitialize]
public void Setup()
{
using (StreamReader r = new StreamReader(#".\currency2.json"))
{
_json = r.ReadToEnd();
}
...
They run fine locally. I have 'Copy always set' but when the tests are ran using Teamcity I get an error saying that it can't find them in a temp folder. They are copied over to the build server but they're not in this temp folder.
Could not find file 'E:\TeamCity\buildAgent\temp\buildTmp\SYSTEM_SERVER 2016-07-18 15_28_19\Out\currency2.json'
I have **\bin\release\*test*.dll setup as my Test File Names in the test build step.
Any help appreciated.

I had a similar problem.
I changed the properties of the test file to this
Build Action = Content
Copy to Output Directory = Copy always
Teamcity will copy the file to the build folder, but it does not seem to maintain the same structure you'd expect.
So I created a file lookup loop. That will step down the expected folder until it finds the text file in question.
var pathLookups = new string[]
{
"2ndFolder\\3rdFolder\\test.json", // folder that normally workes
"3rdFolder\\test.json",
"test.json"
};
foreach (var pathLookup in pathLookups)
{
try
{
jsonFileCollection = JsonFileLoader<TestJsonType>.LoadJson(pathLooksup);
if (jsonFileCollection!= null)
{
break;
}
}
catch (Exception)
{
Console.WriteLine("Attempted to load test json from path:" + pathLooksup);
}
}
It's not the cleanest solution, but it will get the job done. You could refactor this to look a little nicer.

You might pass the full pass by argument to your program (and value defined in TeamCity).
Something like this (this is a pseudo-code example only) :
string[] programArgs;
static void Main(string[] args)
{
programArgs = args
}
[TestInitialize]
public void Setup()
{
using (StreamReader r = new StreamReader(programArgs[1]))
{
_json = r.ReadToEnd();
}
...
}

Related

What is the replacement for TestContext.DataRow["MyColumnName"]

Using MSTest in a .Net Core Unit test project. I am attempting to use a csv datasource to provide the data for a test method.
Previously, I would use something like below in a .Net Framework test project:
[DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", #"data.csv", "data#csv", DataAccessMethod.Sequential),
DeploymentItem("data.csv"),
TestMethod]
public void ValuesController_Post()
{
_controller.Post(TestContext.DataRow["body"]);
_valuesRepository.Verify(_ => _.Post(It.IsAny<string>()), Times.Once);
}
The key here being the DataRow property found in TestContext. This doesn't appear to exist in the .Net Core version of the TestContext.
How would I go about doing this in .Net Core?
Since moving to aspnet core, I've never been able to use the same [Datasource(...)] attribute to iterate through test data, my data-driven tests are always skipped.
Have you considered switching to another approach with [DataTestMethod] and [DynamicData] with a custom source that reads you file ?
Here's a good article on this :
https://www.meziantou.net/2018/02/05/mstest-v2-data-tests
Maybe another way would be to read the whole file at the begining of the test and then iterate through the dataset as One single unit test?
Hope this helps.
It took me an afternoon to fiddle with things, but I finally found a solution. Since you don't specify your test or CSV file, here is a quick example I could get working.
Long story short, I installed the CsvHelper NuGet package, because parsing CSV is dead easy right up to the point it is not. As Carl Verret pointed out, you need to use the [DynamicData(...)] attribute above your test method, and then parse the CSV using CsvHelper.
The CSV File (Example.csv)
A,B,IsLessThanZero
1,2,FALSE
3,-5,TRUE
Important: Make sure this CSV file is included in your test project and "Copy To Output Directory" is set to "Always" in the properties for the CSV file in Solution Explorer.
Data Transfer Object Used By CsvHelper
public class AdditionData
{
public int A { get; set; }
public int B { get; set; }
public bool IsLessThanZero { get; set; }
}
The Test Class
[TestClass]
public class ExampleTests
{
// HINT: Look in {Your Test Project Folder}\bin\{Configuration}\netcore3.1\FolderYourCsvFileIsIn for the CSV file.
// Change this path to work with your test project folder structure.
private static readonly string DataFilePath = Path.GetDirectoryName(typeof(ExampleTests).Assembly.Location) + #"\FolderYourCsvFileIsIn\Example.csv";
[TestMethod]
[DynamicData(nameof(GetData), DynamicDataSourceType.Method)]
public void AddingTwoNumbers(AdditionData data)
{
bool isLessThanZero = data.A + data.B < 0;
Assert.AreEqual(data.IsLessThanZero, isLessThanZero);
}
private static IEnumerable<object[]> GetData()
{
using var stream = new StreamReader(DataFilePath);
using var reader = new CsvReader(stream, new CsvConfiguration(CultureInfo.CurrentCulture));
var rows = reader.GetRecords<AdditionData>();
foreach (var row in rows)
{
yield return new object[] { row };
}
}
}
After building your solution, you will see a single test in Test Explorer. Running this single test runs all variants defined in your CSV file:

How to persist a solution with projects and documents using Roslyn's AdhocWorkspace?

I've been trying to persist a new solution containing a project and a simple cs file with the following code but nothing get saved to the disk. Am I doing something wrong or is Roslyn not the tool to be used to generate solutions project and files?
class Program
{
static void Main(string[] args)
{
var workspace = new AdhocWorkspace();
var solutionInfo = SolutionInfo.Create(SolutionId.CreateNewId(),
VersionStamp.Create(),
#"C:\Seb\SebSol.sln");
var projectInfo = ProjectInfo.Create(ProjectId.CreateNewId(),
VersionStamp.Create(),
"SebProj",
"SebProj.dll",
LanguageNames.CSharp,
#"C:\Seb\SebSol\SebProj\SebProj.csproj");
workspace.AddSolution(solutionInfo);
workspace.AddProject(projectInfo);
var sourceText = SourceText.From("public class A { }");
workspace.CurrentSolution.AddDocument(DocumentId.CreateNewId(projectInfo.Id), "ClassA.cs", sourceText);
workspace.TryApplyChanges(workspace.CurrentSolution);
}
}
You're looking for MsBuildWorkspace, which can actually update sln and csproj files in MSBuild format on disk.
Other than that class, Roslyn APIs are completely agnostic to project formats such as MSBuild.

C# Generating dynamic executable from project

I wanna generate an exe file with some changes in code from another C# exe.
I know that can easy compile .cs single class using CodeDom.Compiler
The thing I want to know is how to compile a project with 'Resources', 'Settings', 'Forms' and other elements.
CSharpCodeProvider.CompileAssemblyFromSource(CompilerParameters, sources[]);
So, the question is where can I add all resources, settings and form (.resx)?
And can I do it with byte[] streams. Without unpacking project's zip.
Sorry for bad English and mby stupid questions. I wish somebody will help me...
For Example: I have byte[] array of resource file 'pic.png' and I wanna attach it to compiled exe as embedded resource.
You should learn about the new compiler service provided by Microsoft in Microsoft.CodeAnalysis code name "Roslyn".
Roslyn provides you the way to compile the code and everything on the fly including creating and compiling complete solution and projects in-memory.
I think what you're looking for can be achieved via Roslyn. See below sample:
class Program
{
static void Main()
{
var syntaxTree = SyntaxTree.ParseCompilationUnit(
#"using System;
using System.Resources;
namespace ResSample
{
class Program
{
static void Main()
{
ResourceManager resMan = new ResourceManager(""ResSample.Res1"", typeof(Program).Assembly);
Console.WriteLine(resMan.GetString(""String1""));
}
}
}");
var comp = Compilation.Create("ResTest.exe")
.AddReferences(new AssemblyNameReference("mscorlib"))
.AddSyntaxTrees(syntaxTree);
var resourcePath = "ResSample.Res1.resources"; //Provide full path to resource file here
var resourceDescription = new ResourceDescription(
resourceName: "ResSample.Res1.resources",
dataProvider: () => File.OpenRead(resourcePath),
isPublic: false);
var emitResult = comp.Emit(
executableStream: File.Create("ResTest.exe"),
manifestResources: new[] { resourceDescription });
Debug.Assert(emitResult.Success);
}
Original Source here
At line dataProvider: () => File.OpenRead(resourcePath), you can provide your own 'FileStream' like () => return _myResourceStream) for your resource file.

Changing the base path of an assembly from the bin directory to a different directory

I have a Test.cs file in C:\ This test file reads from an input file and writes the same to an output file.
Test.cs
public class Test
{
public static int Main(string[] args)
{
var reader = new StreamReader("in.txt");
string input = reader.ReadLine();
var writer = new StreamWriter("out.txt");
writer.WriteLine(input);
return 0;
}
}
Here it should be noted that the code only uses the filename and not the full file path, which means the file is expected to be in the directory where the program is running. And I have created the in.txt in C:\
Now, there is a c# code called Runner.cs in a solution in C:\Project\Runner.cs, that dynamically compiles the Test.cs code and runs it using reflection. Now, when the Test.cs runs, it expects the in.txt file to be in C:\Project\bin\Debug\in.txt , but it is actually present in C:\in.txt
So, my question is, is there a way to make the code to get the file from C:\in.txt and not from the bin directory without changing the path of the file in the Test.cs code file.
Edit: It is my bad that I forgot to mention why I am in need of this requirement.
The Test.cs file comes from over the wire. And I felt it will not be a good choice to edit this file and set the file path accordingly. I want to compile it and run it as it is.
I hope I am clear. If not, please feel free to ask for more information.
If it is as simple as you show in your code switching the CurrentDirectory works for this example:
var mainMembers = new CSharpCodeProvider()
.CreateCompiler()
.CompileAssemblyFromSource(
new CompilerParameters { GenerateInMemory = true }
, #"
using System;
using System.IO;
public class M {
public static int Main() {
Console.WriteLine(""CurDir = ""+ Environment.CurrentDirectory);
var reader = new StreamReader(""in.txt"");
string input = reader.ReadLine();
var writer = new StreamWriter(""out.txt"");
writer.WriteLine(input);
return 0;
}
}")
.CompiledAssembly
.GetType("M")
.GetMember("Main");
// inspect
Environment.CurrentDirectory.Dump("current");
// keep
var oldcd = Environment.CurrentDirectory;
// switch
Environment.CurrentDirectory = "c:\\temp";
// invoke external code
((MethodInfo) mainMembers[0]).Invoke(null,null);
// restore
Environment.CurrentDirectory = oldcd;
In a multi threaded scenario this becomes unreliable.

nunit test working directory

I have the following code (sample1.evol - file attached to my unit test project):
[Test]
public void LexicalTest1()
{
var codePath = Path.GetFullPath(#"\EvolutionSamples\sample1.evol");
//.....
}
I found that the working directory of test execution is not the assembly directory: (in my case codepath variable assigned to d:\EvolutionSamples\sample1.evol).
So, how can I change the execution working directory (without hardcode)? What will be the best practice to load any files attached to test case?
You can use following to get the directory of assembly running the code something like
var AssemblyDirectory = TestContext.CurrentContext.TestDirectory
I use this for integration tests that need to access data files.
On any machine the test needs to run create a system environment variable named TestDataDirectory that points to the root of where your test data is.
Then have a static method that gets the file path for you..
public static class TestHelper
{
const string EnvironmentVariable = "TestDataDirectory";
static string testDataDir = Environment.GetEnvironmentVariable(EnvironmentVariable);
public static string GetTestFile(string partialPath)
{
return Path.Combine(testDataDir, partialPath);
}
}
...
[Test]
public void LexicalTest1()
{
var codePath = TestHelper.GetTestFile(#"\EvolutionSamples\sample1.evol");
//.....
}
I am using this code:
var str = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
if (str.StartsWith(#"file:\")){
str = str.Substring(6);
}
Getting in str variable the assembly directory.
We were having a problem where tests run using ReSharper and NCrunch would work, but the native VS Test Runner would not be able to find the files, when given just a relative file path for the test to use. I solved it by creating a function that you pass the relative test file path into, and it will give you the absolute file path.
private static string _basePath = Path.GetDirectoryName(typeof(NameOfYourTestClassGoesHere).Assembly.Location);
private string GetAbsoluteTestFilePath(string relativePath) => Path.Combine(_basePath, relativePath);
You would then use the function like so:
var input = File.ReadAllLines(GetAbsoluteTestFilePath(#"TestData/YourTestDataFile.txt"));

Categories