如何对此方法进行单元测试(使用剪贴板和文件系统)?

时间:2018-05-03 12:26:45

标签: c# unit-testing

我有一个像组件一样的资源管理器,它实际上处理文件系统。如何开始将单元测试编写到这些需要使用剪贴板的方法,以及文件系统中文件的实际移动或复制?

我尝试过看教程和在线课程,但是它们太抽象了,对我当前的问题无法帮助我。

public void DoCtrlV(object obj)
{
    try
    {
        StopWait();
        var list = System.Windows.Clipboard.GetFileDropList();
        if (list.Count > 0) // i.e. from outside - get file list
        {
            if (!PasteFromOutsideSucceeded(list))
                return;
        }
        else // i.e. from inside
        {
            string targetFolder = GetTargetFolder();

            if (Globals.ListInternalUseCopy != null) // from list
            {
                PasteFromList(targetFolder);
            }
            else if (Globals.TreeInternalUseCopy != null) // from tree
            {
                PasteFromTree(targetFolder);
            }
        }

        RefreshBothControls();
    }
    catch (UnauthorizedAccessException ex)
    {
        // doesn't matter atm
    }
}

编辑/更新:我必须做很多事情,而且我还处于中点,但这是我到目前为止所做的。

  1. 我将所有可测试的代码提取到一个单独的类中,在窗口之外的代码隐藏之前是它(因为你需要实例化窗口来实际测试,所以测试的东西非常糟糕)。我在构造函数中传递了它运行所需的所有必需对象。
  2. 我的DoCtrlV现在看起来像这样:

    StopWait();
    var list = System.Windows.Clipboard.GetFileDropList();
    var result = _hadnler.PerformPaste(Globals.ListInternalUseCopy, Globals.TreeInternalUseCopy, list, MoveNotCopy, shellList.Path, shellTree.SelectedPath);
    if (!result)
        MessageBox.Show("Please select a destiantion.");
    RefreshBothControls();
    

    我唯一测试(atm)的是_hadnler.PerformPaste。

    1. 正如您所看到的,我现在决定忽略剪贴板和其他因素,只关注FileSystem操作。我的PerformPaste现在看起来像这样:

      public bool PerformPaste(List<string> listInternalUseCopy, string treeInternalUseCopy, StringCollection clipboardList, bool moveNotCopy, string listPath, string treePath)
      {
          if (clipboardList.Count > 0) // i.e. from outside - get file list
          {
              if (!PasteFromOutsideSucceeded(clipboardList, listPath))
                  return false;
          }
          else // i.e. from inside
          {
              string targetFolder = GetTargetFolder(listPath, treePath);
      
              if (listInternalUseCopy != null) // from list
              {
                  PasteFromList(targetFolder, moveNotCopy);
              }
              else if (treeInternalUseCopy != null) // from tree
              {
                  PasteFromTree(targetFolder, moveNotCopy);
              }
          }
          return true;
      }
      
    2. 我的PerformPaste仍然调用子方法,但现在它们都是&#34; Ok&#34;。最后,主要的问题是你得到了File.Move或File.Copy - 这就是我需要处理它的原因。所以我安装了System.IO.Abstractions NuGet包,并更改了我的代码以支持IFileSystem(基本上通过在我的新IFileSystem对象调用中添加来替换所有IO调用)。在实际代码中,它被设置为常规文件系统,在testcode中,它是一个模拟文件系统(也由一个名为System.IO.Abstractions.TestingHelpers的子NuGet包支持)。

    3. 我的测试代码现在看起来像这样:

      public class MainLogicHandlerTests
      {
          private readonly MockFileSystem fileSystem;
      
          public MainLogicHandlerTests()
          {
              fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
              {
                  { @"c:\src\file1.txt", new MockFileData("Test1") },
                  { @"c:\src\file2.txt", new MockFileData("Test2") },
                  { @"c:\dst\file3.txt", new MockFileData("Test3") }
              });
              FileSystem.UsedFileSystem = fileSystem;
          }
      
          [TestMethod()]
          public void PasteOneFile_FromOutsideIntoList_ShouldSucceed()
          {
              //Arrange
              List<string> listInternal = new List<string>() { @"c:\src\file1.txt"};
              string treeInternal = null;
              StringCollection clipboardList = new StringCollection();
              clipboardList.Add(@"c:\src\file1.txt");
              bool moveNotCopy = false;
              string listPath = @"c:\dst";
              string treePath = @"c:\dst";
      
              //Act
              var sut = new MainLogicHandler(null);
              var result = sut.PerformPaste(listInternal, treeInternal, clipboardList, moveNotCopy, listPath, treePath);
      
              //Assert
              Assert.IsTrue(fileSystem.FileExists(@"c:\dst\file1.txt"));
          }
      }
      

      所以还有很长的路要走,但至少这是一个开始。将会感谢你们有更多的提示......

1 个答案:

答案 0 :(得分:6)

这就是我通常对单元测试这样做的事情。 从我的方法中获取依赖于非单元可测试内容的代码。

例如,System.Windows.Clipboard.GetFileDropList不受您的控制,不应进行单元测试(它是外部依赖项)。为了使其不受单元测试的影响,您需要使用依赖注入。

创建一个类似于此的界面:

public interface IClipboard
{
    StringCollection GetClipBoardInfo();
}

将从此继承的类注入DoCtrlV方法。

public void DoCtrlV(object obj, IClipboard clipboard)

创建一个实现该方法的接口版本的类,并返回正确的剪贴板函数:

public StringCollection GetClipBoardInfo()
{
  System.Windows.Clipboard.GetFileDropList(); 
}

现在,为单元测试创​​建一个类,它只返回一个StringCollection并告诉你该方法是否被调用。

public MockClipboard
{
  public StringCollection GetClipBoardInfo()
  {
    ClipboardWasRead = true;

    StringCollection col = new StringCollection();
    col.Add("...");

    return col;
  }

  public bool ClipboardWasRead { get; set; }
}

这样您就不会对不在您控制范围内的东西进行单元测试(如剪贴板和文件系统)。如果要测试方法是否读取剪贴板,可以测试方法的ClipboardWasRead布尔值。 这里真正重要的是你需要重写你的方法以使其更具单元可测试性。你现在编写的代码有很多外部依赖,例如:

  • System.Windows.Clipboard.GetFileDropList
  • PasteFromOutsideSucceeded
  • RefreshBothControls

一次只能处理它们,直到您觉得您的代码可以根据自己的喜好进行测试为止。

这是一个示例测试,它将告诉您方法是否实际尝试读取剪贴板。

[TestMethod]
public void WasClipboardRead()
{
  // Arrange
  var mock = new MockClipboard();
  var obj = new Object();

  // Act
  DoCtrlV(obj, mock);

  // Assert
  Assert.IsTrue(mock.ClipboardWasRead);
}