Not able to write JSON data to file using JSON.Net - c#

I am trying to write data into a json file using C# and JSON.NET referring the accepted answer suggested here (How to write a JSON file in C#?).
When I run the code below I am not getting any error message but also the data is not written to the json file.
Functionally what I am trying to achieve is creating a json file that stores times for actions that are done while executing Nunit test
I have tried to implement the solution suggested here (Deserializing JSON data to C# using JSON.NET) but it did not solve my problem
public static class ActionTimeHelper
{
private static readonly string _actionTimeLogFileName = "ActionTimeLog_" + string.Format("{0:yyyy_MM_dd_hhmmss}", DateTime.Now);
[ThreadStatic] private static FileStream _fileStream = null;
[ThreadStatic] private static StreamWriter _actionStreamWriter = null;
[ThreadStatic] private static JsonWriter _jsonWriter = null;
[ThreadStatic] private static List<ActionTimeInfo> actionList = new List<ActionTimeInfo>();
public static void CreateActionTimeLogFile(string logPath, string testName)
{
string dir = logPath + testName + #"\";
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
_fileStream = File.Open(dir + _actionTimeLogFileName + ".json", FileMode.CreateNew);
_actionStreamWriter = new StreamWriter(_fileStream);
_jsonWriter = new JsonTextWriter(_actionStreamWriter);
_jsonWriter.Formatting = Formatting.Indented;
JsonSerializer serializer = new JsonSerializer();
serializer.Serialize(_jsonWriter, actionList.ToArray().ToString());
//var jarray = JsonConvert.DeserializeObject<List<ActionTimeInfo>>(actionList.ToArray().ToString());
}
public static void StartActionTime(string actionName)
{
actionList.Add(new ActionTimeInfo()
{
ActionName = actionName,
StartTime = DateTime.Now
});
}
public static void EndActionTime(string actionName)
{
ActionTimeInfo endAction = actionList.Find(actionInfo => actionInfo.ActionName.Equals(actionName));
endAction.EndTime = DateTime.Now;
endAction.ExecutionTime = endAction.EndTime.Subtract(endAction.StartTime);
}
}
public class ActionTimeInfo
{
public string ActionName { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public TimeSpan ExecutionTime { get; set; }
}
Usage of above classes in UUnit test:
[Test, Parallelizable, RequiresThread]
public void TestMethod3()
{
ActionTimeHelper.StartActionTime("Login Action");
ActionTimeHelper.EndActionTime("Login Action");
}
[TearDown]
public void TestTearDown()
{
ActionTimeHelper.CreateActionTimeLogFile(TCRunSettings.LogPath, TestContext.CurrentContext.Test.Name);
}
Actual Result: JSON file created in the directory but no list items written to it
Expected Result: JSON file created in the directory along with list items written to it

I think you're missing flush and close for _jsonWriter?
maybe try
_jsonWriter.Flush();
_jsonWriter.Close();
at the end of the method
or even better, use using to wrap the _fileStream, _actionStreamWriter and _jsonWriter

Related

How can the FileInfo class be mocked using System.IO.Abstractions?

I have injected the System.IO.Abstractions.IFileSystem interface into a class so that I can unit test file system interactions. There is one place in the class that uses new FileInfo(fileName). What is the replacement for that when using the IFileSystem interface and MockFileSystem?
Replacing File.OpenRead with _fileSystem.File.OpenRead is simple...
public string? Decrypt(string encryptedFilePath, string privateKeyArmor, string passPhrase)
{
try
{
using var privateKeyStream = new MemoryStream(Encoding.ASCII.GetBytes(privateKeyArmor));
using var encryptedFileStream = _fileSystem.File.OpenRead(encryptedFilePath);
var inputStream = PgpUtilities.GetDecoderStream(encryptedFileStream);
...
...but I don't know how to replace new FileInfo(fileName) here.
private byte[] CompressFile(string fileName, CompressionAlgorithmTag algorithm)
{
var outputStream = new MemoryStream();
var compressedDataGen = new PgpCompressedDataGenerator(algorithm);
PgpUtilities.WriteFileToLiteralData(compressedDataGen.Open(outputStream), PgpLiteralData.Binary,
new FileInfo(fileName));
...
I tried _fileSystem.FileInfo.FromFileName(fileName), but that returns IFileInfo instead of FileInfo and the WriteFileToLiteralData method won't take that.
There is a helper function FileInfo.New(string fileName) which can be used to create/use a mock IFileInfo object
public class FileInfoTest
{
private readonly IFileSystem _fileSystem;
public FileInfoTest()
: this (new FileSystem())
{
}
internal FileInfoTest(IFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
public bool GetIsReadOnly(string path)
{
var info = _fileSystem.FileInfo.New(path);
return info.IsReadOnly;
}
}
To demonstrate this I have a physical file which is not read-only.
The first test, returns the IsReadonly state of the physical file.
The second, returns a mocked IFileInfo object with IsReadOnly set to true.
[TestMethod]
public void CheckFileInfoAgainstPhysicalFile()
{
var tester = new FileInfoTest();
var isReadOnly = tester.GetIsReadOnly(#"c:\dev\File.txt");
Assert.IsFalse(isReadOnly);
}
[TestMethod]
public void CheckFileInfoAgainstMock()
{
var mockFileInfo = new Mock<IFileInfo>();
mockFileInfo.SetupGet(mk => mk.IsReadOnly).Returns(true);
var mockFileSystem = new Mock<IFileSystem>();
mockFileSystem.Setup(mk => mk.FileInfo.New(#"c:\dev\File.txt")).Returns(mockFileInfo.Object);
var tester = new FileInfoTest(mockFileSystem.Object);
var isReadOnly = tester.GetIsReadOnly(#"c:\dev\File.txt");
Assert.IsTrue(isReadOnly);
}
As mentioned in the comment, the above doesn't address the basic problem - PgpUtilities doesn't know what an IFileInfo is.
There is a way to fix this but it may not be worth the effort.
Define an interface for the methods used from the static class.
Inject this interface.
In the default implementation of `IPgpUtilities`, use reflection to get at the `FileInfo` instance inside the `FileInfoWrapper` and pass it through to the external routine.
// Stand-in for External utility (returns a string so we can see it
// doing something with the original file)
public static class PgpUtilitiesOriginal
{
public static string WriteFileToLiteralData(Stream outputStream,
char fileType,
FileInfo file)
{
return file.Name;
}
}
// Interface for injection
public interface IPgpUtilties
{
string WriteFileToLiteralData(Stream outputStream,
char fileType,
IFileInfo file);
}
// Wrapper for the External Utility
public class DefaultPgpUtilities : IPgpUtilties
{
public string WriteFileToLiteralData(Stream outputStream, char fileType, IFileInfo file)
{
var instanceInfo = file.GetType().GetField("instance", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var instance = (FileInfo)instanceInfo.GetValue(file);
return PgpUtilitiesOriginal.WriteFileToLiteralData(outputStream, fileType, instance);
}
}
// Test Target
public class Tester
{
private readonly IFileSystem _fileSystem;
private readonly IPgpUtilties _pgpUtilities;
public Tester()
: this(new FileSystem(), new DefaultPgpUtilities())
{
}
public Tester(IFileSystem fileSystem, IPgpUtilties pgpUtilities)
{
_fileSystem = fileSystem;
_pgpUtilities = pgpUtilities;
}
public string Run(string fileName)
{
return _pgpUtilities.WriteFileToLiteralData(null, '\0', _fileSystem.FileInfo.FromFileName(fileName));
}
}
[TestMethod]
public void PhysicalFile()
{
var tester = new Tester();
var ret = tester.Run(#"c:\dev\file.txt");
Assert.AreEqual("file.txt", ret);
}
[TestMethod]
public void MockedFile()
{
var mockFileObject = new Mock<IFileInfo>();
var mockFileSystem = new Mock<IFileSystem>();
mockFileSystem.Setup(mk => mk.FileInfo.FromFileName(#"c:\dev\file.txt")).Returns(mockFileObject.Object);
var mockPgpUtilties = new Mock<IPgpUtilties>();
mockPgpUtilties.Setup(mk => mk.WriteFileToLiteralData(It.IsAny<Stream>(), It.IsAny<char>(), mockFileObject.Object)).Returns("Hello World");
var tester = new Tester(mockFileSystem.Object, mockPgpUtilties.Object);
var ret= tester.Run(#"c:\dev\file.txt");
Assert.AreEqual("Hello World", ret);
}
Again, sorry about the piss-poor reading of the original question on my part.

How to Serialize T type in Json file

I want to save the information of a model in the Json file I want to implement this in such a way that I can easily store different models So I used this method:
public abstract class SettingBase
{
[JsonIgnore]
public abstract string Filename { get; }
public static T Load<T>() where T : SettingBase, new()
{
T result = new T();
result = JsonFile.Load<T>(result.Filename) ?? result;
return result;
}
public void Save()
{
JsonFile.Save(Filename, this);
}
}
JsonFile:
public static class JsonFile
{
public static void Save<T>(string fileName, T #object)
{
using (StreamWriter writer = File.CreateText(fileName))
{
string json = JsonSerializer.Serialize(#object);
writer.Write(json);
}
}
public static T Load<T>(string fileName)
{
using (StreamReader reader = File.OpenText(fileName))
{
string json = reader.ReadToEnd();
return JsonSerializer.Deserialize<T>(json);
}
return default(T);
}
}
now i can use like this:
var xx = SettingBase.Load<AppModel>();
xx.boo = true;
xx.integ = 25;
xx.str = "sss";
xx.Save();
The problem is that the save operation is not performed and nothing is saved in the json file
How to solve this problem?
Unfortunately, this feature is not currently supported on .NET Core Probably supported in .Net 6.0 You must use newtonsoft.json
public static void Save<T>(string fileName, T #object)
{
var json = JsonSerializer.Serialize(#object);
System.IO.File.WriteAllText(fileName, json);
}
public static T Load<T>(string fileName)
{
var json = System.IO.File.ReadAllText(fileName);
return JsonSerializer.Deserialize<T>(json)
}

C# How to use lambda expression with dictionary's value which is a method

I'm creating a program which will execute a command after user input.
Some commands I want to implement are: creating, reading a file, getting current working directory etc.
I created a dictionary which will store user input and corresponding command:
public static Dictionary<string, Action<string[]>> Commands { get; set; } = new Dictionary<string, Action<string[]>>()
{
{"pwd", PrintWorkingDirectory },
{"create", CreateFile },
{"print", ReadFile },
};
Unfortunately I have issues with triggering the method:
public void Run()
{
Console.WriteLine("Welcome, type in command.");
string input = null;
do
{
Console.Write("> ");
input = Console.ReadLine();
Execute(input);
} while (input != "exit");
}
public int Execute(string input)
{
if(Commands.Keys.Contains(input))
{
var action = Commands.Values.FirstOrDefault(); //doesn't work, gives '{command} not found'
}
Console.WriteLine($"{input} not found");
return 1;
}
Also I noticed that this solution would not work with method which is not void, but returns something, as for example CreateFile.
public static string CreateFile(string path)
{
Console.WriteLine("Create a file");
string userInput = Console.ReadLine();
try
{
string[] file = userInput.Split(new char[] { ' ' }).Skip(1).ToArray();
string newPath = Path.GetFullPath(Path.Combine(file));
using (FileStream stream = new FileStream(newPath, FileMode.Create, FileAccess.ReadWrite))
{
stream.Close();
}
using (StreamWriter sw = new StreamWriter(newPath))
{
Console.WriteLine("Please type the content.Press Enter to save.");
sw.WriteLine(Console.ReadLine());
sw.Close();
Console.WriteLine("File {0} has been created", newPath);
}
}
catch (Exception)
{
throw;
}
return path;
}
public static void ReadFile(string[] args)
{
Console.WriteLine("Reading file");
string userInput = Console.ReadLine();
string[] file = userInput.Split(new char[] { ' ' }).Skip(1).ToArray();
string newPath = Path.GetFullPath(Path.Combine(file));
string[] lines = File.ReadAllLines(newPath);
foreach (string line in lines)
Console.WriteLine(line);
}
public static void PrintWorkingDirectory(string[] args)
{
var currentDirectory = Directory.GetCurrentDirectory();
Console.WriteLine(currentDirectory);
}
Could somebody advise me how to deal with these issues?
Is it that this dictionary I created does not make much sense at all?
First problem: You're always fetching the first element of the dictionary and are not using the index operator to retrieve the correct value. Therefore change:
if(Commands.Keys.Contains(input))
{
var action = Commands.Values.FirstOrDefault(); //doesn't work, gives '{command} not found'
}
to:
public int Execute(string input)
{
if (Commands.Keys.Contains(input))
{
var action = Commands[input]; //doesn't work, gives '{command} not found'
action?.Invoke(new string[] { });
}
else
{
Console.WriteLine($"{input} not found");
}
return 1;
}
Regarding to your second question about dictionary usage. I think it is ok to use a dictionary to map different commands based on a given key. The alternative would be switch or if constructs, which can be prevented in Object Oriented Programming.
Regarding to your question about string CreateFile(string path). Since C# is strongly typed language your dictionary can only contain objects of type Action<string[]>, so you can't use methods with another signature than that. One solution is to add another dictionary in the form of Dictionary<string,Func<string[], string>. As a result you'll get more and more dictionaries depending on your method signatures. From here on you should think to build to encapsulate your commands in an e.g. CommandInterpreter class, that could offer an API like that:
void Request(string cmdName, string[] cmdParameters);
string GetLastResult();
int GetLastCode();
Update:
Below code shows a possible object oriented solution (I've left out interfaces to make the code more compact):
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp1
{
public class Command<T>
{
public string Name { get; }
public T TheCommand { get; }
public Command(string name, T theCommand)
{
Name = name;
TheCommand = theCommand;
}
}
public interface ICommandResult
{
void Ok(Action<ICommandResult> yes, Action<ICommandResult> no);
int Code { get; }
string Description { get; }
}
public abstract class CommandResult : ICommandResult
{
public int Code { get; }
public string Description { get; }
protected CommandResult(int code, string description)
{
Code = code;
Description = description;
}
public abstract void Ok(Action<ICommandResult> yes, Action<ICommandResult> no);
}
public class NullCommandResult : CommandResult
{
public NullCommandResult() : base(-1, "null")
{
}
public override void Ok(Action<ICommandResult> yes, Action<ICommandResult> no) => no?.Invoke(this);
}
public class SuccessCommandResult : CommandResult
{
public SuccessCommandResult(string description) : base(0, description)
{
}
public override void Ok(Action<ICommandResult> yes, Action<ICommandResult> no) => yes?.Invoke(this);
}
public class CommandInterpreter
{
private Dictionary<string, Func<IEnumerable<string>, ICommandResult>> Commands = new Dictionary<string, Func<IEnumerable<string>, ICommandResult>>();
public void RegisterCommand(Command<Func<IEnumerable<string>, ICommandResult>> cmd)
=> Commands.Add(cmd.Name, cmd.TheCommand);
public ICommandResult RunCommand(string name, IEnumerable<string> parameters)
=> Commands.Where(kvp => kvp.Key.Equals(name))
.Select(kvp => kvp.Value)
.DefaultIfEmpty(strArr => new NullCommandResult())
.Single()
.Invoke(parameters);
}
class Program
{
private CommandInterpreter _cmdInterpreter;
private Program()
{
_cmdInterpreter = new CommandInterpreter();
_cmdInterpreter.RegisterCommand(new Command<Func<IEnumerable<string>, ICommandResult>>("pwd", PrintWorkingDirectory));
_cmdInterpreter.RegisterCommand(new Command<Func<IEnumerable<string>, ICommandResult>>("create", CreateFile));
_cmdInterpreter.RegisterCommand(new Command<Func<IEnumerable<string>, ICommandResult>>("print", ReadFile));
}
private static CommandResult ReadFile(IEnumerable<string> arg) => new SuccessCommandResult("File read");
private static CommandResult CreateFile(IEnumerable<string> arg) => new SuccessCommandResult("File xyz created");
private static CommandResult PrintWorkingDirectory(IEnumerable<string> arg) => new SuccessCommandResult("Printed something");
static void Main() => new Program().Run();
private void Run()
{
Console.WriteLine("Welcome, type in command.");
string input;
do
{
Console.Write("> ");
input = Console.ReadLine();
var cmdResult = _cmdInterpreter.RunCommand(input, Enumerable.Empty<string>());
cmdResult.Ok(
r => Console.WriteLine($"Success: {cmdResult.Code}, {cmdResult.Description}"),
r => Console.WriteLine($"FAILED: {cmdResult.Code}, {cmdResult.Description}"));
} while (input != "exit");
}
}
}
Output:
Welcome, type in command.
> pwd
Success: 0, Printed something
> create
Success: 0, File xyz created
> abc
FAILED: -1, null
>
You can just copy the code and play around with it.

Object reference error while instantiating non static class in multiple threads

I am using C# and NUnit framework for executing multiple test in separate threads simultaneously. I want to record the time between certain actions and have created an ActionTimeHelper class for the same.
Below is the code for the class and the context in which methods in the class are used
When I run two test in parallel bot of which call the Login method then only one test completes and the other one throws an error
(Message: System.NullReferenceException : Object reference not set to an instance of an object.
) at actionList.Add(new ActionTimeInfo()
I have made the actionList as ThreadStatic so that every test which runs on its own thread has its own copy, so I am not able to figure out why am getting the error.
Note: If I run one test at a time everything works fine
Can someone please guide me on this. Thanks for the help.
Have read some articles Referencing a non static member with an instantiated object, Referencing an instantiated object in a static class (c#) but not able to relate ti my specific problem
public static class ActionTimeHelper
{
private static readonly string _actionTimeLogFileName = "ActionTimeLog_" + string.Format("{0:yyyy_MM_dd_hhmmss}", DateTime.Now);
[ThreadStatic] private static FileStream _fileStream = null;
[ThreadStatic] private static StreamWriter _actionStreamWriter = null;
[ThreadStatic] private static JsonWriter _jsonWriter = null;
[ThreadStatic] private static List<ActionTimeInfo> actionList = new List<ActionTimeInfo>();
public static void CreateActionTimeLogFile(string logPath, string testName)
{
string dir = logPath + testName + #"\";
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
_fileStream = File.Open(dir + _actionTimeLogFileName + ".json", FileMode.CreateNew);
_actionStreamWriter = new StreamWriter(_fileStream);
_jsonWriter = new JsonTextWriter(_actionStreamWriter);
_jsonWriter.Formatting = Formatting.Indented;
JsonSerializer serializer = new JsonSerializer();
serializer.Serialize(_jsonWriter, actionList);
_fileStream.Flush();
_actionStreamWriter.Flush();
_jsonWriter.Flush();
}
public static void StartActionTime(string actionName)
{
actionList.Add(new ActionTimeInfo()
{
TestName = TestContext.CurrentContext.Test.Name,
ActionName = actionName,
StartTime = DateTime.Now
});
}
public static void EndActionTime(string actionName)
{
ActionTimeInfo endAction = actionList.Find(actionInfo => actionInfo.ActionName.Equals(actionName));
endAction.EndTime = DateTime.Now;
endAction.ExecutionTime = endAction.EndTime.Subtract(endAction.StartTime);
}
}
public class ActionTimeInfo
{
public string TestName { get; set; }
public string ActionName { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public TimeSpan ExecutionTime { get; set; }
}
public void Login(string username, string password)
{
ActionTimeHelper.StartActionTime("Navigating to URL");
ActionTimeHelper.EndActionTime("Navigating to URL");
ActionTimeHelper.StartActionTime("Login Action");
ActionTimeHelper.EndActionTime("Login Action");
Thread.Sleep(30000);
}
[TearDown]
public void TestTearDown()
{
ActionTimeHelper.CreateActionTimeLogFile(LogPath, TestContext.CurrentContext.Test.Name);
}
Actual result: Null reference error
Expected result: No null reference error
“Do not specify initial values for fields marked with ThreadStaticAttribute, because such initialization occurs only once, when the class constructor executes, and therefore affects only one thread. If you do not specify an initial value, you can rely on the field being initialized to its default value if it is a value type, or to null if it is a reference type.“
From
https://learn.microsoft.com/en-us/dotnet/api/system.threadstaticattribute?view=netframework-4.8

Approvaltests and PDF

Can I use ApprovalTests with PDF's? I tried using the FileLauncher but it seems the identical PDF's are slightly different at file (bit) level. Or did I use it wrongly?
[TestMethod]
[UseReporter(typeof(FileLauncherReporter))]
public void TestPdf()
{
var createSomePdf = PdfCreate();
ApprovalTests.Approvals.Verify(new FileInfo(createSomePdf.FileName));
}
The Pdf is most likely being created with a timestamp. Depending on the method used to create the pdf, you might be able to mock out the created time. but I had to scrub it.
Here's the code I used to do that.
public static void VerifyPdf(string coverFile)
{
ScrubPdf(coverFile);
Approvals.Verify(new ExistingFileWriter(coverFile));
}
private static void ScrubPdf(string coverFile)
{
long location;
using (var pdf = File.OpenRead(coverFile))
{
location = Find("/CreationDate (", pdf);
}
using (var pdf = File.OpenWrite(coverFile))
{
pdf.Seek(location, SeekOrigin.Begin);
var original = "/CreationDate (D:20110426104115-07'00')";
var desired = new System.Text.ASCIIEncoding().GetBytes(original);
pdf.Write(desired, 0, desired.Length);
pdf.Flush();
}
}
I found a command-line tool, diff-pdf. Compares 2 PDFs and returns exit code 0 if they're the same, 1 if they differ. Download + extract + add it to your PATH.
Downside - it must render both PDFs to perform the diff. If they're big, perf hit.
Approver (based heavily on ApprovalTests.Approvers.FileApprover):
public class DiffPdfApprover : IApprovalApprover
{
public static void Verify(byte[] bytes)
{
var writer = new ApprovalTests.Writers.BinaryWriter(bytes, "pdf");
var namer = ApprovalTests.Approvals.GetDefaultNamer();
var reporter = ApprovalTests.Approvals.GetReporter();
ApprovalTests.Core.Approvals.Verify(new DiffPdfApprover(writer, namer), reporter);
}
private DiffPdfApprover(IApprovalWriter writer, IApprovalNamer namer)
{
this.writer = writer;
this.namer = namer;
}
private readonly IApprovalNamer namer;
private readonly IApprovalWriter writer;
private string approved;
private ApprovalException failure;
private string received;
public virtual bool Approve()
{
string basename = string.Format(#"{0}\{1}", namer.SourcePath, namer.Name);
approved = Path.GetFullPath(writer.GetApprovalFilename(basename));
received = Path.GetFullPath(writer.GetReceivedFilename(basename));
received = writer.WriteReceivedFile(received);
failure = Approve(approved, received);
return failure == null;
}
public static ApprovalException Approve(string approved, string received)
{
if (!File.Exists(approved))
{
return new ApprovalMissingException(received, approved);
}
var process = new Process();
//settings up parameters for the install process
process.StartInfo.FileName = "diff-pdf";
process.StartInfo.Arguments = String.Format("\"{0}\" \"{1}\"", received, approved);
process.Start();
process.WaitForExit();
if (process.ExitCode != 0)
{
return new ApprovalMismatchException(received, approved);
}
return null;
}
public void Fail()
{
throw failure;
}
public void ReportFailure(IApprovalFailureReporter reporter)
{
reporter.Report(approved, received);
}
public void CleanUpAfterSucess(IApprovalFailureReporter reporter)
{
File.Delete(received);
if (reporter is IApprovalReporterWithCleanUp)
{
((IApprovalReporterWithCleanUp)reporter).CleanUp(approved, received);
}
}
}
To Verify:
DiffPdfApprover.Verify(pdfBytes);
diff-pdf can visually show diffs as well. I rolled a Reporter for this, but don't use it much. I think it'll come in handy if there are regressions after initial report dev (which is where I'm at right now).
public class DiffPdfReporter : GenericDiffReporter
{
private static readonly string Path = FindFullPath("diff-pdf.exe");
public DiffPdfReporter() : base(Path,
GetArgs(),
"Please put diff-pdf.exe in your %PATH%. https://github.com/vslavik/diff-pdf. And restart whatever's running the tests. Everything seems to cache the %PATH%.") { }
private static string GetArgs()
{
return "--view \"{0}\" \"{1}\"";
}
private static string FindFullPath(string programInPath)
{
foreach (var path in from path in Environment.GetEnvironmentVariable("path").Split(';')
select path)
{
var fullPath = System.IO.Path.Combine(path, programInPath);
if (File.Exists(fullPath))
return fullPath;
}
return null;
}
}
Looks like this is built in to ApprovalTests now.
usage:
Approvals.VerifyPdfFile(pdfFileLocation);
See the source:
public static void VerifyPdfFile(string pdfFilePath)
{
PdfScrubber.ScrubPdf(pdfFilePath);
Verify(new ExistingFileWriter(pdfFilePath));
}

Categories

Resources