我正确使用嘲笑吗?

时间:2011-11-04 08:43:11

标签: c# tdd mocking moq

我是第一次嘲笑和尝试moq的新手。我想知道我是否只能使用模拟编写测试?

public class FileCopierTests
{
    private string path = AppDomain.CurrentDomain.BaseDirectory;

    [Fact]
    public void Copy_starts_copying_when_event_is_fired_returns_true()
    {
        var fullyQualifiedFileName = string.Format(@"{0}\..\..\Integration\TestData.xls", this.path);
        var destFileName = Path.GetTempPath() + "66768c06-d1c4-4416-be81-767f36abeeb1.xls";

        var copierMock = new Mock<IFileCopier>();
        var watcherMock = new Mock<IFileWatcher>();

        copierMock.Setup(cp => cp.Copy(fullyQualifiedFileName, destFileName)).Raises(
            ev => ev.CopyingFinished += null, destFileName);

        watcherMock.Object.Changed += arg => copierMock.Object.Copy(arg, destFileName);   

        watcherMock.Raise(e => e.Changed += null, fullyQualifiedFileName);

        copierMock.VerifyAll();
    }
}

这些是我想要测试的类和接口,

public interface IFileCopier
{
    event Action<string> CopyingFinished;

    void Copy(string fqFileName, string destFileName);
}

public class FileCopier : IFileCopier
{
    private ReaderWriterLockSlim @lock = new ReaderWriterLockSlim();

    public event Action<string> CopyingFinished;

    public void Copy(string fqFileName, string destFileName)
    {
        this.@lock.EnterWriteLock();
        try
        {
            File.Copy(fqFileName, destFileName, true);

            if (this.CopyingFinished == null)
            {
                return;
            }

            this.CopyingFinished(destFileName);
        }
        finally
        {
            this.@lock.ExitWriteLock();
        }
    }
}

public interface IFileWatcher
{
    event Action<string> Changed;
}

public class FileChangeWatcher : IFileWatcher
{
    public FileChangeWatcher(FileSystemWatcher eyes)
    {
        this.Eyes = eyes;
        this.Eyes.Changed += this.OnChangedEvent;
    }

    protected FileSystemWatcher Eyes { get; set; }

    public event Action<string> Changed;

    private void OnChangedEvent(object sender, FileSystemEventArgs eventArgs)
    {
        if (this.Changed == null)
        {
            return;
        }

        this.Changed(eventArgs.FullPath);
    }
}

2 个答案:

答案 0 :(得分:8)

你正在做的事情是不正确的 - 当你只使用模拟编写测试时,你并没有真正测试任何东西!

而你想要的是介绍一些非模拟代码,代码使用你用模拟代替的对象。

使用模拟的两件大事(特别是模拟框架,如moq)给你的是:

1 - 隔离

2 - 可验证性

通过隔离,我的意思是,在您的情况下,每次在文件复印机类上测试操作时,您都不希望真正复制文件。因此,您可以使用伪装成文件复制器的东西替换真正的文件复制器。

通过可证实性我的意思是,通常课程没有任何方式告诉你他们是否被召唤。这通常是一件好事,因为告诉其他人他们是否被调用不属于他们的工作,但在测试场景中你需要这些信息 - moq中的期望允许你收集它。


在这种情况下,看起来你有一些东西会引发一个事件,然后是一些侦听这个事件并在你的FileCopier上调用copy方法的东西。

因此,您希望模拟事件源(或以正确的方式调用事件的具体来源),然后引发该事件。

然后您需要第二个模拟 - FileCopier的模拟,期望使用正确的参数调用其复制方法。应该将此FileCopier模型传递给调用它的类(依赖注入),这样可以确认实际代码调用了复制方法。

答案 1 :(得分:3)

你只能有嘲笑,但这意味着你实际上没有测试任何东西。

在这种情况下,对copier的引用会更好地成为真正的类,这样就可以在watchermock关闭事件时测试事情发生。