I have a function, that returns text file path and file content:
public static Tuple<string, string> OpenTextFile()
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog .Filter = "Text |*.txt";
bool? accept = openFileDialog.ShowDialog();
if (accept == true)
return Tuple.Create(File.ReadAllText(openFileDialog.FileName, Encoding.UTF8), openFileDialog.FileName);
else
return null;
}
How can I unit test file reading? And is it possible to test dialog showing?
That method is tightly coupled to multiple concerns. The OpenFileDialog
is a UI concern and File
is an IO concern. This makes testing the functionality of that method in isolation difficult but not impossible.
Extract those concerns into their own abstractions.
public interface IOpenFileDialog {
string Filter { get; set; }
bool? ShowDialog();
string FileName { get; set; }
}
public interface IFileSystem {
string ReadAllText(string path, Encoding encoding = Encoding.UTF8);
}
I would also suggest converting that static method into a service method
public interface ITextFileService {
Tuple<string, string> OpenTextFile();
}
Its implementation would depend on the other abstractions
public class TextFileService : ITextFileService {
readonly IOpenFileDialog openFileDialog;
readonly IFileSystem file;
public SUT(IOpenFileDialog openFileDialog, IFileSystem file) {
this.openFileDialog = openFileDialog;
this.file = file;
}
public Tuple<string, string> OpenTextFile() {
openFileDialog.Filter = "Text |*.txt";
bool? accept = openFileDialog.ShowDialog();
if (accept.GetValueOrDefault(false))
return Tuple.Create(file.ReadAllText(openFileDialog.FileName, Encoding.UTF8), openFileDialog.FileName);
else
return null;
}
}
The implementations of the dependencies would then wrap their respective concerns.
This would also allow all the abstractions to be mocked/replaced when testing their dependents in isolation.
Here is an example of testing the method using MSTest and Moq based on the above suggestions.
[TestMethod]
public void _OpenTextFile_Should_Return_TextContext_And_FileName() {
//Arrange
var expectedFileContent = "Hellow World";
var expectedFileName = "filename.txt";
var fileSystem = new Mock<IFileSystem>();
fileSystem.Setup(_ => _.ReadAllText(expectedFileName, It.IsAny<Encoding>()))
.Returns(expectedFileContent)
.Verifiable();
var openFileDialog = new Mock<IOpenFileDialog>();
openFileDialog.Setup(_ => _.ShowDialog()).Returns(true).Verifiable();
openFileDialog.Setup(_ => _.FileName).Returns(expectedFileName).Verifiable();
var sut = new TextFileService(openFileDialog.Object, fileSystem.Object);
//Act
var actual = sut.OpenTextFile();
//Assert
fileSystem.Verify();
openFileDialog.Verify();
Assert.AreEqual(expectedFileContent, actual.Item1);
Assert.AreEqual(expectedFileName, actual.Item2);
}
instead of directly testing the UI, which crosses a boundary, you want to abstract away the UI.
perhaps :-
interface IFileSelector
{
string Filter {get; set'}
bool? SelectFile()
string FileSelected
}
then
public static Tuple<string, string> OpenTextFile(IFileSelector selector)
{
selector.Filter = "Text |*.txt";
bool? accept = selector.SelectFile()
if (accept == true)
return Tuple.Create(File.ReadAllText(selector.FileSelected, Encoding.UTF8), selector.FileSelected);
else
return null;
}
then make a UI one
public class WinformsFileSelector : IFileSelector
{
...
}
and then use a mocking framework for testing like Moq
Yes, you can create a test file to read from in your test project. I generally put stuff like this in a folder called Assets.
Then instead of an OpenFileDialog, you just specify the exact path to the test file's location and pass that into your function and validate it with asserts as per the usual.
However I don't think this function needs a Unit Test. It's pretty explicit in what it does.
I feel like what you do with the tuple this function returns is what you should be testing, in which case your Unit test should just create the tuple by hand, and do your logic with that.
IE your test would probably start with:
TestFunction() {
var TestString = "My Test data";
var testTuple = new Tuple.Create(TestString, "Name");
Assert.That(MyTupleLogic(testTuple), Is.Whatever());
}
You don't really need to test that Microsoft's Tuple.Create and OpenFileDialog work, which is what the test you propose would be checking.
I only bother running unit tests on functions that have logic I entered
Your method has two concerns: selecting a file in UI and reading it's content.
SelectTextFile
and ReadAllText
. IFileDialog.SelectTextFile
and IFileSystem.ReadAllText
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.