簡體   English   中英

單元測試使用外部dll的方法

[英]Unit testing a method that uses an external dll

我有一個名為A的項目,它有一個名為ClassA的類。 ClassA有一個名為ReadBlock()的方法,它創建一個CloudBlockBlob對象並調用其中一個方法。

CloudBlockBlob是一個位於Microsoft.WindowsAzure.Szrage.Blob命名空間的類,該命名空間位於Microsoft.WindowsAzure.Storage.dll中

我的項目A有一個名為A.Tests的單元測試項目。 現在,我想測試方法ReadBlock() 為了測試它,我需要模擬CloudBlockBlob對象並攔截對其方法的調用,返回自定義值並驗證方法是否被調用。

  • 如何模擬在方法內完全創建的對象?
  • 我可以以某種方式更改項目A的DLL引用並將其引用到創建模擬對象而不是真實對象的模擬dll嗎?
  • 我可以在A.Tests類中使用我自己的實現覆蓋項目AMicrosoft.WindowsAzure.Storage.Blob中的類的調用嗎?

更新:問題是我是否可以在不修改項目A的代碼的情況下完成此操作。

謝謝!

如果不修改A類代碼,您將無法使用Moq來修改ReadBlock方法。 您將能夠使用代碼編織工具(MsFakes,Typemock隔離器等)來使用此方法。

例如(MsFakes):

[TestMethod]
public void TestMethod1()
{
    using (ShimsContext.Create())
    {
        ShimCloudBlockBlob.AllInstances.<the method you want to override>  = (<the method arguments>) => {};
    }
}

using范圍內,您將能夠通過屬性AllInstances覆蓋CloudBlockBlob具有的任何方法。

在下一節中,我將討論你擁有的所有其他選項......

選項1:

    public class A
    {
        private IBlockBlob _blockBlob;

        public A(IBlockBlob blockBlob)
        {
            _blockBlob = blockBlob;
        }

        public void ReadBlock()
        {
            _blockBlob.DoSomething();
        }
    }

由於每次調用ReadBlock (您的方法的當前行為)時都會創建一個新實例,因此最好注入工廠而不是包裝器,並且應該create DoSomething ; 選項2:

    public class A
    {
        private readonly IFactoryBlockBlob _blobFctory;

        public A(IFactoryBlockBlob blobFctory)
        {
            _blobFctory = blobFctory;
        }

        public void ReadBlock()
        {
           var blob =  _blobFctory.Create();
        }
    }

但是,根據您的問題和您的評論,您的班級似乎“有依賴”而不是“需要依賴”。

在此輸入圖像描述

(馬克西門子寫了一本關於DI的好書,這張圖取自他的書

有了這條新信息,您的方法應該是這樣的; 選項3:

    public class A
    {
        public void ReadBlock(ICloudBlob blob)
        {
        }
    }

但是你不想改變方法的簽名:

    public class A
    {

        public void ReadBlock()
        {
            ReadBlock(new CloudBlockBlob(<the params bla bla...>));
        }

        internal void ReadBlock(ICloudBlob blob)
        {
        }
    }

添加InternalsVisibleToAttribute ,然后驗證內部方法的行為。

通過閱讀這些內容,我覺得您的課程是一種“遺留代碼”,意味着它可以完成工作,不會改變,並且驗證其行為可能是浪費時間。 在過去,我發布了一個圖表( 在這個答案中 ),可以幫助您決定處理此案例的方法。

最好為CloudBlockBlob創建一個非常簡單的可模擬包裝器,以提高代碼的可測試性並使用依賴性反轉注入它。

現在你可能有類似的東西:

public class A
{
    public void ReadBlock()
    {
        var blockBlob = new CloudBlockBlob();
        blockBlob.DoSomething();
    }
}

相反,將您的包裝器注入A中,以便A不知道對CloudBlockBlob的依賴:

public class A
{
    IBlockBlob _blockBlob

    public A(IBlockBlob blockBlob)
    {
        _blockBlob = blockBlob;
    }

    public void ReadBlock()
    {
        _blockBlob.DoSomething();
    }
}

免責聲明,我在Typemock工作。

您可以在不使用Isolator修改項目A代碼的情況下執行此操作。 有一個簡單的例子如何完成:

public class Foo
{
    public void ReadBlock()
    {
        var block = new CloudBlockBlob(new Uri("http://myUrl/%2E%2E/%2E%2E"));
        var name = block.Name;
    }
}

[TestMethod, Isolated]
public void TestReadBlock()
{
    //Arrange
    var fakeBlock = Isolate.Fake.AllInstances<CloudBlockBlob>();
    Isolate.WhenCalled(() => fakeBlock.Name).WillReturn("Name");

   //Act
   var foo = new Foo();
   foo.ReadBlock();

   //Assert
   Isolate.Verify.WasCalledWithAnyArguments(() => fakeBlock.Name);
}

希望能幫助到你!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM