单元测试模拟框架会强迫您进行编程吗?

时间:2016-05-17 09:12:16

标签: c# unit-testing moq

我正在使用Moq框架设置单元测试。

我有一个描述类的接口,如下所示:

public interface ISourceFileLocation : IFileLocation, IDisposable
{
    bool RemoveAfterTransfer { get; set; }
    void RemoveSource();
    //.....
}

为了确保调用RemoveSource-Method,我实现了一个基类,其中在dispose方法中调用remove。

public abstract class SourceFileBase : ISourceFileLocation
{
    //......

    public bool RemoveAfterTransfer { get; set; }

    public void RemoveSource()
    {
        if (File.Exists(Uri.AbsolutePath))
        {
            File.Delete(Uri.AbsolutePath);
        }
    }

    public void Dispose()
    {
        if (this.RemoveAfterTransfer)
            this.RemoveSource();
    }
}

当模拟ISourceFileLocation时,没有默认实现,所以对于测试我想在具体类应该继承的基类上实现测试。

当模拟基类时,我的测试期望RemoveSource-Method是虚拟的,这打破了我确保调用该方法的想法!!

这是否缺乏框架,是否有更好的框架或测试方法,或者这是我的代码的问题,我应该重新考虑我的设计吗?

亲切的问候。

测试方法:

    [TestCategory("Source")]
    [TestMethod]
    public void CheckIfRemoveSourceMethodIsCalled()
    {
            //ARRANGE
            var mockSourceLocation = new Mock<SourceFileBase>();
            mockSourceLocation.Object.RemoveAfterTransfer = true;
            mockSourceLocation.Setup(x => x.RemoveSource());
            //ACT
            mockSourceLocation.Object.Dispose();
            /ASSERT
            mockSourceLocation.VerifyAll();
    }

    [TestCategory("Source")]
    [TestMethod]
    public void CheckIfRemoveSourceMethodIsNotCalled()
    {
            //ARRANGE
            var mockSourceLocation = new Mock<SourceFileBase>();
            mockSourceLocation.Object.RemoveAfterTransfer = false;
            //ACT
            mockSourceLocation.Object.Dispose();
            //ASSERT
            mockSourceLocation.Verify(x=>x.RemoveSource(), Times.Never);
    }

1 个答案:

答案 0 :(得分:6)

我认为您可能在此示例中滥用Moq,因为您正在为被测系统(SUT)使用相同的模拟对象,并执行行为验证。您的测试主要是检查SourceFileBase是否自行调用。通常,您将使用Moq来验证对SUT的依赖项的调用。

SourceFileBase中的代码不是单元测试友好的,因为它直接在File类上使用静态方法。要使其可单元测试,您需要以某种方式对其进行抽象(例如,请参阅this question)。

public abstract class SourceFileBase : ISourceFileLocation
{
    private readonly IFileSystem _fileSystem;

    public SourceFileBase(IFileSystem fileSystem)
    {
        _fileSystem = fileSystem;
    }

    ...

    public void RemoveSource()
    {
        _fileSystem.DeleteFile(Uri.AbsolutePath);
    }

    public void Dispose()
    {
        RemoveSource();
    }
}

这样做可以让您测试Dispose()RemoveSource()都删除该文件。

// Arrange
var mockFileSystem = new Mock<IFileSystem>();
var sut = new Mock<SourceFileBase>(mockFileSystem.Object);
sut.RemoveAfterTransfer = true;
sut.Uri = myTestUri;

// Act
sut.Dispose();

// Assert
mockFileSystem.Verify(f => f.DeleteFile(myTestUri.AbsolutePath));