使用nSubstitute时未调用Abstracted Dispose方法

时间:2017-08-17 09:54:57

标签: c# unit-testing abstract-class nsubstitute

我有这个基础(抽象)类:

public abstract class StreamWriterBuilderService : IStreamWriterService
{
    private Stream fs;
    private StreamWriter stream;

    public void WriteLine(
        string stringToWrite )
    {
        if ( stream == null )
        {
            throw new InvalidOperationException( "Tried to call WriteLine on an uninitialised StreamWriterBuilderService" );
        }

        stream.WriteLine( stringToWrite );
        Flush();
    }

    public void Flush()
    {
        if ( stream == null )
        {
            throw new InvalidOperationException( "Tried to call Flush on an uninitialised StreamWriterBuilderService" );
        }

        stream.Flush();
    }

    public void Initialise(
        string filePath )
    {
        Contract.Requires<ArgumentNullException>( filePath != null );

        fs = File.Open( filePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite );
        stream = new StreamWriter( fs );
        WriteLine("Initialised");
        Initialised = true;
    }

    public bool Initialised { get; private set; }

    #region Dispose Implementation

    ~StreamWriterBuilderService()
    {
        Dispose( false );
        Debug.WriteLine( false, "This StreamWriterBuilderService object was not disposed of" );
    }

    private bool _isDisposed;

    public virtual void Dispose()
    {
        Dispose( true );
        GC.SuppressFinalize( this );
    }

    protected virtual void Dispose(
        bool disposing )
    {
        if ( _isDisposed )
            return;

        if ( disposing && Initialised )
        {
            stream.Dispose();
            fs.Dispose();
            stream = null;
            fs = null;
        }

        _isDisposed = true;
    }

    #endregion Dispose Implementation
}

我正在对此进行单元测试:

    protected readonly string TargetFilePath = Path.GetTempFileName();
    protected const string Header = "Initialised";

    protected override void SetContext()
    {
        SUT = Substitute.For<StreamWriterBuilderService>();
    }

和...

    private string fileContents;
    private const string TestString = @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!£$%^&*()+~@?><|¬";

    protected override void Because()
    {
        base.Because();
        SUT.Initialise( TargetFilePath );
        SUT.WriteLine( TestString );
        SUT.Flush();
        SUT.Dispose();
        fileContents = File.ReadAllText( TargetFilePath );
    }

我遇到的麻烦就是:

SUT.Dispose();

实际上并没有在我的抽象类中调用我的Dispose方法,因为它被声明为虚方法。如果我删除虚拟方法,则代码将被播放。但是,我需要将虚拟方法保留在那里,因为我需要在我的解决方案中覆盖其他地方......

2 个答案:

答案 0 :(得分:2)

您应该使用:

has_one

这会让您fine control超过您想要替换的特定内容。 因此,默认情况下,您的var SUT = Substitute.ForPartsOf<StreamWriterBuilderService>(); 方法不会被替换。

或者,我的建议(针对此特定方案)将构建您自己的Dispose类(继承自StreamWriterBuilderServiceForTesting)。 因为不参与NSubstitute,你可以根据自己的喜好定义它的行为。

答案 1 :(得分:1)

当您substitute这样的类时,虚拟方法将替换为替换。您可能希望部分替换仅替换Dispose(bool)

Documentation about partial substitutes

the second Paragraph here

  

对于初学者来说,NSubstitute只能使用该类的虚拟成员,因此该类中的任何非虚拟代码都将实际执行!

也就是说,虚拟方法将执行。

为了测试目的,我创建了一个派生类型,只是为了删除abstract,然后用它进行测试。