WinForms Unit Testing - c#

I have been given an old large WinForm application and been asked to write a unit testing project for it.
Asside: The code uses .xlsx files as scripts and these scripts can be complex. My initial (standard practice) idea was to make each test atomic, however, the way the methods that required testing are invoked are complex and dependent on many other code components. So I have decided to invoke a script in order to test the required components...
The code was not written with unit testing in mind and in order to invoke a test I need to first open an .xlsx file; this means my test will have the following structure:
[TestMethod]
public void SomeTest()
{
WorkbookSet workbookSet = Factory.GetWorkbookSet(CultureInfo.CurrentCulture);
Workbook workbook = workbookSet.Workbooks.Add();
workbookSet.GetLock();
try
{
// Open the script.
string testScriptPath = Path.Combine(
Environment.CurrentDirectory, "Scripts\\Test.xlsx");
SSGForm doc = new SSGForm();
if (FileExists(testScriptPath))
OpenExistingWb(ref doc, ref workbookSet, ref workbook, testScriptPath);
// Do testing here...
}
finally
{
workbookSet.ReleaseLock();
}
}
For all test we will have to have the workbookSet/workbook initalisation and Get/ReleaseLock() actions. In my own WPF applications, I have not come accros this problem of code replication before as I have designed the apps to be unit testable from the outset. My question is is there an standard approach in testing of wrapping the actual test code (// Do testing here...) with the boiler plate?
Of course in an actual code I could use a strategy/factory pattern with an interface or merely inject a Func<T, T>(or Action for a void return) into a wrapper method, but is this viable for testing?
Thanks for your time.
Edit. Something like:
private void RunTest(Action action)
{
WorkbookSet workbookSet = Factory.GetWorkbookSet(CultureInfo.CurrentCulture);
Workbook workbook = workbookSet.Workbooks.Add();
workbookSet.GetLock();
try
{
string testScriptPath = Path.Combine(
Environment.CurrentDirectory, "Scripts\\Test.xlsx");
SSGForm doc = new SSGForm();
if (Utils.FileExists(testScriptPath))
mainRibbonForm.OpenExistingWb(ref doc, ref workbookSet, ref workbook, testScriptPath);
// Run the test logic.
action();
}
finally
{
workbookSet.ReleaseLock();
}
}
[TestMethod]
public void SomeTest()
{
RunTest(() =>
{
// Logic here.
});
}

You described the method in your question; to pass the SSGForm to your action, you can just use the Action<T> delegate:
private void RunTest(Action<SSGForm> action)
{
WorkbookSet workbookSet = Factory.GetWorkbookSet(CultureInfo.CurrentCulture);
Workbook workbook = workbookSet.Workbooks.Add();
workbookSet.GetLock();
try
{
string testScriptPath = Path.Combine(
Environment.CurrentDirectory, "Scripts\\Test.xlsx");
SSGForm doc = new SSGForm();
if (Utils.FileExists(testScriptPath))
mainRibbonForm.OpenExistingWb(ref doc, ref workbookSet, ref workbook, testScriptPath);
// Run the test logic.
action(doc);
}
finally
{
workbookSet.ReleaseLock();
}
}
[TestMethod]
public void SomeTest()
{
RunTest(doc =>
{
// Logic here.
Foo(doc);
});
}

Related

Is it possible to build your TestCaseSource list inside SetUp using NUnit?

Can I build my TestCaseData list in my SetUp? Because with this setup my test is just being skipped. Other regular tests are running just fine.
[TestFixture]
public class DirectReader
{
private XDocument document;
private DirectUblReader directReader;
private static UblReaderResult result;
private static List<TestCaseData> rootElementsTypesData = new List<TestCaseData>();
[SetUp]
public void Setup()
{
var fileStream = ResourceReader.GetScenario("RequiredElements_2_1.xml");
document = XDocument.Load(fileStream);
directReader = new DirectUblReader();
result = directReader.Read(document);
// Is this allowed?
rootElementsTypesData.Add(new TestCaseData(result.Invoice.Id, new IdentifierType()));
rootElementsTypesData.Add(new TestCaseData(result.Invoice.IssueDate, new IdentifierType()));
}
[Test, TestCaseSource(nameof(rootElementsTypesData))]
public void Expects_TypeOfObject_ToBeTheSameAs_InputValue(object inputValue, object expectedTypeObject)
{
Assert.That(inputValue, Is.TypeOf(expectedTypeObject.GetType()));
}
}
As stated by #IMil, the answer is No... that's not possible.
TestCaseSource is used by NUnit to build a list of the tests to be run. It associates a method with a particular set of arguments. NUnit then creates an internal representation of all your tests.
OTOH SetUp (and even OneTimeSetUp is used when those tests are being run. By that time, the number of tests and the actual arguments to each of them are fixed nothing can change them.
So, in order to do what you seem to want to do, your TestCaseSource has to stand on it's own, fully identifying the arguments to be used for the test. That's why NUnit gives you the capability of making the source a method or property, rather than just a simple list.
In your case, I suggest something like...
private static IEnumerable<TestCaseData> RootElementsTypesData()
{
var fileStream = ResourceReader.GetScenario("RequiredElements_2_1.xml");
document = XDocument.Load(fileStream);
directReader = new DirectUblReader();
result = directReader.Read(document);
yield return new TestCaseData(result.Invoice.Id, new IdentifierType()));
yield return new TestCaseData(result.Invoice.IssueDate, new IdentifierType()));
}
Obviously, this is only "forum code" and you'll have to work with it to get something that actually compiles and works for your case.
No, this is impossible.
Methods decorated with [SetUp] are run before each test case.
This means NUnit will first build list of test cases, then run Setup() before each of them.
Therefore, your Setup() never gets called, and list of test cases remains empty.

How to user Assert for methods with return type void in MSTest

I have a method in my c# application similar to below.
public async Task SampleMethod()
{
try
{
//some code
await AnotherMethod();
// some code
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.Message.ToString());
}
}
Now, I'm trying to write a unit testcase for the above method using MStest. I have written something as below.
[TestMethod]
public async Task SampleMethodTest()
{
ClassName cn = new ClassName();
await cn.SampleMethod();
}
Now how do I know if the testcase failed or succeeded. How do I use Assert here?
Any help is highly appreciated.
Based on our comments in my other answer, i try to show you how to get the console output. That you can read all text from console you have to set a StringWriter() to the console:
[TestMethod]
public async Task SampleMethodTest()
{
using (StringWriter stringWriter = new StringWriter())
{
Console.SetOut(stringWriter);
ClassName cn = new ClassName();
await cn.SampleMethod();
string consoleOutput = stringWriter.ToString();
Assert.IsFalse(consoleOutput.Contains("Exception"));
}
}
I hope this works. I haven't tried it with a UnitTest, only with a console program.
If you test the AnotherMethod directly, you will see if it's succefull. When it throws an Exception the test is failed. The SampleMethod does only implement the try catch and calls the AnotherMethod() which can be tested directly.
[TestMethod]
public async Task SampleMethodTest()
{
ClassName cn = new ClassName();
await cn.AnotherMethod();
}
This test fail if it throws an Execption. When the method do not throw an Exception, it is successfull.
If your method changes the state of the object, you can verify if the state of the object is like expected. If not you can use a Mock (with a Framework like Moq) to verify the collaboration with other objects. Note that you maybe need to extract AnotherMethod to another class, so that you can mock and verify the call.
Also note that you should try to design your Software so that you can use Outputverification and Stateverification in most UnitTests. Communication Verification with mocks can lead to false postives and UnitTests that are hard to maintain.

What are good ways to unit test interaction with the filesystem?

I'm working on a simple project more as an exercise in TDD than anything else. The program fetches some images from a web server and saves them as files. For the record, what I am doing (my desired end result) is very similar to this perl script but in C#.
I've got to the point where I need to save the files to disk. I need to make unit tests to mandate the code. I'm not sure how to approach this. I want to be able to verify that the code created the expected files with the expected file name(s) and of course I don't want to touch the file-system at all. I'm not completely new to unit testing and TDD but for some reason I'm really not clear what to do in this situation. I'm sure the answer will be obvious once I've seen it but.... the mysterious place in my brain where code comes from is just not cooperating.
My tools of choice are MSpec and FakeItEasy, but suggestions in any frameworks would be gratefully received. What are sensible approaches to unit testing file system interactions?
What would help here is Dependency Injection. Break up the monolithic download operation into smaller pieces and inject them into the downloader. Declare interfaces for these pieces:
public interface IImageFetcher
{
IEnumerable<Image> FetchImages(string address);
}
public interface IImagePersistor
{
void StoreImage(Image image, string path);
}
With these declarations you can write a downloader class that integrates the whole thing like this:
public class ImageDownloader
{
private IImageFetcher _imageFetcher;
private IImagePersistor _imagePersistor;
// Constructor injection of components
public ImageDownloader(IImageFetcher imageFetcher, IImagePersistor imagePersistor)
{
_imageFetcher = imageFetcher;
_imagePersistor = imagePersistor;
}
public void Download(string source, string destination)
{
var images = _imageFetcher.FetchImages(source);
int i = 1;
foreach (Image img in images) {
string path = Path.Combine(destination, "Image" + i.ToString("000"));
_imagePersistor.StoreImage(img, path);
i++;
}
}
}
Note that ImageDownloader does not know which implementations will be used and how they work.
Now, you can supply a dummy persistor when testing, that stores the filenames in a List<string> for instance, instead of supplying the real one that stores to the file system.
UPDATE
// For testing purposes only.
class DummyImagePersistor
{
public readonly List<string> Filenames = new List<string>();
public void StoreImage(Image image, string path)
{
Filenames.Add(path);
}
}
Testing:
var persistor = new DummyImagePersistor();
var sut = new ImageDownloader(new ImageFetcher(), persistor);
sut.Download("http://myimages.com/images", "C:\Destination");
Assert.AreEqual(10, persistor.Filenames.Count);
...

Mocking a StreamWriter/determining when to mock

I have a class which uses a StreamWriter to write to a file.
public void CreateLog(string errorLogFilePath, StringBuilder errorLogBuilder, string errorMessage)
{
using (StreamWriter sw = new StreamWriter(errorLogFilePath, true)
{
errorLogBuilder.Apend("An error was discovered.");
//....
sw.Write(errorLogBuilder.ToString());
}
}
[Questions]
1: Is it possible to check that the .Write() method is called?
2: Do i need to wrap a MemoryStream inside the StreamWriter in order to test it, without actually accessing the hard drive. One of StreamWriters constructors accepts a stream but it states the following + will the UTF-8 encoding affect this?
Initializes a new instance of the StreamWriter class for the specified stream by using UTF-8 encoding and the default buffer size.
3: How do you determine if a class is actually accessing the hd and thus needs to be mocked? (sorry if this last question sounds stupid, but im genuinely a little puzzled by this.)
Have the method write to a TextWriter rather than a StreamWriter. Then test the method by passing it a mock TextWriter. In the "real" code, of course, you'll pass in a StreamWriter that was created using new StreamWriter(errorLogFilePath, true).
This yields the following answers to your questions:
Yes
No
You can't generally determine that without decompiling its code.
A little more detail:
Refactor the method into two methods:
public void CreateLog(string errorLogFilePath, StringBuilder errorLogBuilder, string errorMessage)
{
using (StreamWriter sw = new StreamWriter(errorLogFilePath, true)
{
CreateLog(sw, errorLogBuilder, errorMessage);
}
}
public void CreateLog(TextWriter writer, StringBuilder errorLogBuilder, string errorMessage)
{
errorLogBuilder.Apend("An error was discovered.");
//....
writer.Write(errorLogBuilder.ToString());
}
Test the first method to ensure that it calls the second method with an appropriately-constructed StreamWriter. Test the second method to ensure that it calls Write on the passed TextWriter, with appropriate arguments. Now you've abstracted away the dependency on the hard drive. Your tests don't use the hard drive, but you're testing everything.
Generally speaking, you could :
Use a well tested logging library (like NLog, MS Logging Application Block), and spare you developping and maintaining your own.
Refactor your logging logic (or code calling messageboxes, open file dialogs, and so on) into a service, with its interface. This way you can split your testing strategy :
when testing consumers of the loggin service : mock the logging interface to make sure the log method is called. This will ensure that the logging is correctly called by consumers of your logging service
when testing the logging service implementation, just make sure that expected output matches given input : if you want to write "FOO" to bar.log, effectively call
IE :
// arrrange
File.Delete("bar.log")
// act
CreateLog("bar.log", errorLogBuilder, "FOO")
// assert
Assert.IsTrue( File.Exists("bar.log") )
Assert.IsTrue( File.ReadAllLines("bar.log").First() == "FOO")
The point is making sure that the component is called, done by mocking.
Then you can check that the component works as expected.
I know this is a very old question, but I came across this while trying to solve a similar problem.. namely, how to fake the StreamWriter.
The way I went about this was by not having the StreamWriter created inside the method as part of the using statement, but created up front, within the ctor (make your class extend from IDisposable and then destroy the StreamWriter in the Dispose method instead). Then inject a fake over the top of it while under test:
internal class FakeStreamWriter : StreamWriter
{
public List<string> Writes { get; set; } = new List<string>();
public FakeStreamWriter() : base(new MemoryStream()) { }
public override void Write(string value)
{
WriteLine(value);
}
public override void WriteLine(string value)
{
Writes.Add(value);
}
public override void Flush()
{
}
}
My unit test method then looks like this:
public void SmtpStream_Negotiate_EhloResultsCorrectly()
{
var ctx = new APIContext();
var logger = new FakeLogger();
var writer = new FakeStreamWriter();
var reader = new FakeStreamReader { Line = "EHLO test.com" };
var stream = new SmtpStream(logger, ctx, new MemoryStream())
{
_writer = writer,
_reader = reader
};
Exception ex = null;
try
{
stream.Negotiate(ctx);
}
catch (Exception x)
{
ex = x;
}
Assert.IsNull(ex);
Assert.IsTrue(writer.Writes.ElementAt(0) == "250 Hello test.com");
Assert.IsTrue(writer.Writes.ElementAt(1) == "250 STARTTLS");
}

Mocking File calls with Rhino Mock

Is it possible to mock out File calls with rhino mock example:
private ServerConnection LoadConnectionDetailsFromDisk(string flowProcess)
{
var appPath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath;
var bodyFile = Path.Combine(appPath, #"XML\ServerConnections.xml");
if (File.Exists(bodyFile))
{
//more logic
}
So I am trying to mock the File.Exists method so it will return true, so I am able to test the next branch of logic regardless of if the file exists or not. Is this possible?
Here's your original snippet:
private ServerConnection LoadConnectionDetailsFromDisk(string flowProcess)
{
var appPath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath;
var bodyFile = Path.Combine(appPath, #"XML\ServerConnections.xml");
if (File.Exists(bodyFile))
{
//more logic
}
}
Instead of using the System.IO library (which is not possible to mock), cadrell was basically saying to add a layer of abstraction, which you can mock:
private ServerConnection LoadConnectionDetailsFromDisk(string flowProcess)
{
var appPath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath;
var bodyFile = Path.Combine(appPath, #"XML\ServerConnections.xml");
if (FileExists(bodyFile))
{
//more logic
}
}
public bool FileExists(bodyFile) { return File.Exists(bodyFile) }
Now, in your test, you can define a PartialMock that uses most of the existing code (allowing you to test it) but allows you to override just the FileExists method:
var myPartialMock = mockRepo.PartialMock(typeof(MyObject));
myPartialMock.Expect(m=>m.FileExists("")).IgnoreArguments().Return(true);
myPartialMock.LoadConnectionDetailsFromDisk("myProcess");
Now, the call from inside your if statement always returns true.
Something else to consider; I see an if block predicated on the existence of a file. You didn't specify the code, but I would bet anybody else but you (since you can change the code) that the code opens or manipulates the file we now know exists. So, the entire method rubs right up against the border of what you can and can't unit-test. You can consider refactoring this method to obtain a Stream from another function (allowing you to mock that function and inject a MemoryStream with test data), but at some point you'll be scraping the edges of your "sandbox" and will just have to trust that the .NET team did their job and that calls to File.Exists, File.Open etc work as expected.
Abstract it away using an interface.
public Interface IFileChecker
{
bool FileExists(string path)
}
Then use the interface to create your mock object.
IFileChecker fileChecker = mocks.Stub<IFileChecker>();
using (mocks.Record())
{
fileChecker.Stub(i => i.FileExists(Arg<string>.Is.Any)).Return(true);
}

Categories