Unit test method for reading files with OpenFileDialog C #

I have a function that returns the 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 read a unit test file? And can you check the dialogue?

+3


source to share


4 answers


This method is closely related to several problems. OpenFileDialog

is a user interface File

issue and is an I / O issue. This makes testing the functionality of this method in isolation difficult, but not impossible.

Extract these issues into your 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 this static method to a service method

public interface ITextFileService {
    Tuple<string, string> OpenTextFile();
}

      

Its implementation will depend on 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;
    }
}

      

Dependency implementations will then terminate their respective problems.

This will also allow all abstractions to be mocked / replaced when testing their dependents in isolation.

Here's an example of testing a method using MSTest and Moq based on the suggestions above.

[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);
}

      

+3


source


instead of directly testing the UI that crosses the border, you want to distract the UI.

possibly: -

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 create the user interface

public class WinformsFileSelector : IFileSelector
{
...
}

      

and then use a mocking framework for testing like Moq

+2


source


Yes, you can create a test file to read from your test project. I usually put these things in a folder called Assets.

Then, instead of OpenFileDialog, you just give the exact path to the location of the test file and pass it to your function and validate it with assertions as usual.

However, I don't think this function needs a Unit Test. It's pretty clear in what he does.

It seems to me that what you are doing 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 will probably start with:

TestFunction() {
    var TestString = "My Test data";
    var testTuple = new Tuple.Create(TestString, "Name");
    Assert.That(MyTupleLogic(testTuple), Is.Whatever());
}

      

You do not need to check if Microsoft Tuple.Create and OpenFileDialog are working, which is what your test will check.

I am only worried about doing unit tests of the functions in which the logic is injected

+1


source


Your method has two problems: selecting a file in the UI and reading its contents.

  • So first, you better split it into SelectTextFile

    and ReadAllText

    .
  • Then let's see that the second is not actually defined by logic, but only by one .NET call. Not sure if you will have more value if you test it.
  • Second, you have two boundaries, the user interface and the file system, which are not under your control. If you have a lot of time and want to cover almost 100% of the code with unit tests, then, as other answers mentioned, you distract them from some of the interface methods: IFileDialog.SelectTextFile

    andIFileSystem.ReadAllText

  • Use your mocking library of choice, for example. Moq or direct embedded values ​​to simulate some test cases
+1


source







All Articles