Reputation: 1573
I have the following class:
public class DirectoryFinder : IDirectoryFinder
{
public string GetDirectory(string whereTo)
{
FolderBrowserDialog dialog = new FolderBrowserDialog {Description = whereTo};
DialogResult result = dialog.ShowDialog();
return result == DialogResult.OK ? dialog.SelectedPath : string.Empty;
}
}
How would I go about verifying that it returns the correct data? eg. string.Empty or whatever was selected in the dialog depending on what the user click?
I'm using NUnit as testing framework.
Upvotes: 6
Views: 8761
Reputation: 326
I would like to thank Matthew Strawbrige for his excellent answer. I started out with his answer but I found out that because I was using constructor injection (Caliburn Micro) I customized it to suit myself. First the interface. (I've added a set to the SelectedPath so I can set the starting directory).
public interface IFolderBrowserDialogWrapper
{
string SelectedPath { get; set; }
string Description { get; set; }
DialogResult ShowDialog();
}
Then the implementation
public class FolderBrowserDialogWrapper : IFolderBrowserDialogWrapper
{
private FolderBrowserDialog _dialog;
public FolderBrowserDialogWrapper()
{
_dialog = new FolderBrowserDialog();
}
public DialogResult ShowDialog()
{
return _dialog.ShowDialog();
}
public string SelectedPath
{
get { return _dialog.SelectedPath; }
set { _dialog.SelectedPath = value; }
}
public string Description
{
get { return _dialog.Description; }
set { _dialog.Description = value; }
}
}
The constructor injection which will use the normal FolderBrowseDialog from the class above under normal conditions which you will have specified in your injection framework ;-)
public class TestBrowserDialog : Caliburn.Micro.Screen
{
private IFolderBrowserDialogWrapper _folderBrowserDialogWrapper;
public TestBrowserDialog(IFolderBrowserDialogWrapper folderBrowserDialogWrapper)
{
_folderBrowserDialogWrapper = folderBrowserDialogWrapper;
}
...
public void Browse()
{
...
}
...
}
And the command executed. I'm using Caliburn Micro which calls the method directly. In other frameworks, it could a delegate command or something else. My Browse method is linked to a button on the View, so clicking the button on the view, activates the Browse command and opens up a BrowseFolderDialog as wanted.
public void Browse()
{
_folderBrowserDialogWrapper.Description = "Your description goes here";
DialogResult result = _folderBrowserDialogWrapper.ShowDialog();
string path = result == DialogResult.OK ? _folderBrowserDialogWrapper.SelectedPath : string.Empty;
// Process your result.
SourceDirectory = path;
}
A fragment of my test code. Here I can test the behaviour of the Browse command was as expected without bringing up the BrowseFolderDialog during the unit test!
[Test]
public void TestBrowserDialog_Browse_SetsSourceDirectory()
{
// Arrange - This is using NSubstitute for Mocking, NUnit for testing
IFolderBrowserDialogWrapper _folderBrowserDialogWrapper = Substitute.For<IFolderBrowserDialogWrapper>();
_folderBrowserDialogWrapper.ShowDialog().Returns(DialogResult.OK);
string testFileDirectory = Path.Combine(NUnit.Framework.TestContext.CurrentContext.TestDirectory, "Directory 1");
_folderBrowserDialogWrapper.SelectedPath.Returns(testFileDirectory);
// Insert your own _folderBrowserDialogWrapper for testing purposes
TestBrowserDialog sut = new TestBrowserDialog(_folderBrowserDialogWrapper);
// Action
sut.Browse();
// Assert - I'm using FluentAssertions
sut.SourceDirectory.Should().Be(testFileDirectory);
}
I hope this gives someone else ideas on what to do. Matthew Strawbridge's answer has certainly helped me.
Upvotes: 3
Reputation: 20610
One option is to separate out the untestable UI part from the testable business logic:
public string GetDirectory(string whereTo)
{
FolderBrowserDialog dialog = new FolderBrowserDialog { Description = whereTo };
DialogResult result = dialog.ShowDialog();
return GetDirectory(dialog.SelectedPath, result);
}
public string GetDirectory(string selectedPath, DialogResult result)
{
return result == DialogResult.OK ? selectedPath : string.Empty;
}
So you would just test the second method, which becomes easy.
Another option would be to use mocking/faking of the UI components. However, FolderBrowserDialog
is sealed, which makes this harder.
You could do something like this, but it's probably overkill.
First, define an interface for just the parts you want to use:
public interface IFolderBrowserDialogWrapper
{
DialogResult ShowDialog();
string SelectedPath { get; }
}
Then wrap the real FolderBrowserDialog
in your new interface:
public class FolderBrowserDialogWrapper : IFolderBrowserDialogWrapper
{
private readonly FolderBrowserDialog m_dialog;
public DialogResult ShowDialog()
{
return m_dialog.ShowDialog();
}
public string SelectedPath
{
get { return m_dialog.SelectedPath; }
}
public FolderBrowserDialogWrapper(FolderBrowserDialog dialog)
{
m_dialog = dialog;
}
}
And create a fake version for testing, which just returns the values passed into its constructor:
public class FakeFolderBrowserDialogWrapper : IFolderBrowserDialogWrapper
{
private readonly DialogResult m_result;
private readonly string m_selectedPath;
public DialogResult ShowDialog()
{
return m_result;
}
public string SelectedPath
{
get { return m_selectedPath; }
}
public FakeFolderBrowserDialogWrapper(string selectedPath, DialogResult result)
{
m_selectedPath = selectedPath;
m_result = result;
}
}
Then your method can use a FolderBrowserDialogWrapper
for a real dialog:
public string GetDirectory(string whereTo)
{
var f = new FolderBrowserDialogWrapper(
new FolderBrowserDialog { Description = whereTo });
return GetDirectory(f);
}
public string GetDirectory(IFolderBrowserDialogWrapper dialog)
{
DialogResult result = dialog.ShowDialog();
return result == DialogResult.OK ? dialog.SelectedPath : string.Empty;
}
And tests can use a FakeFolderBrowserDialogWrapper
to bypass the UI:
[Test]
public static void TestDirectoryFinderGetDirectoryWithOKExpectThePath()
{
const string expectedPath = @"C:\temp";
var dlg = new FakeFolderBrowserDialogWrapper(expectedPath, DialogResult.OK);
var df = new DirectoryFinder();
string result = df.GetDirectory(dlg);
Assert.That(result, Is.EqualTo(expectedPath));
}
[Test]
public static void TestDirectoryFinderGetDirectoryWithCancelExpectEmptyString()
{
const string expectedPath = @"C:\temp";
var dlg = new FakeFolderBrowserDialogWrapper(expectedPath, DialogResult.Cancel);
var df = new DirectoryFinder();
string result = df.GetDirectory(dlg);
Assert.That(result, Is.EqualTo(string.Empty));
}
But that's probably over-the-top unless you're creating lots of FolderBrowserDialog
s elsewhere in your code as well.
Upvotes: 12