簡體   English   中英

我是否應該創建域對象的接口以在單元測試中隔離它們

[英]Should i create interface of my domain object to isolate them in unit test

單元測試和域對象的接口在SO上都非常常見,只是我發現我做不到的答案無法將兩個概念結合在一起。

根據此主題, 域實體應作為接口還是作為純對象公開?

我沒有理由為我的商品創建界面,但是在這一點上我很難創建測試

我舉一個簡單的例子:

public MyClass1
{
  public int id {get;set;}
  public MyClass2 Class2{get;set;}
}

如果不創建自己的接口,然后使用moq在測試虛擬對象中創建實現,我如何測試MyClass1與MyClass2隔離呢?

MyClass1 mc1 = new MyClass1();
IMyClass2 moqC2 = Moq.Get<IMyCLass2>();
mc1.Id = 1
mc1.Class2 = moqC2 

Aseert.That(mc1.Id, Is.EqualTo(1));
Aseert.That(mc1.Class2 , Is.EqualTo(moqC2 ));

如果MyClass2有一些問題,則在MyClass1上不進行接口測試可能會失敗

編輯

在實際項目中,MyClass1和MyClass2具有20多個屬性(具有顯式的get和set)以及一些執行復雜操作的方法。 在這里,我發布一個基本示例,僅用於談論其參考

編輯2回復jgauffin和Eben Roux好的評論

有關我的案件的更多詳細信息:

根據用戶規范,我的主要對象之一是“活動”,每個活動都有許多狀態,結果,人群以及每個活動的社會,類型和分類屬性。

但是,即使沒有任何活動,此對象的每個對象也可以存在,例如,用戶想要創建具有所有可能狀態的狀態表,並且必須在創建單個活動之前完成此操作,以供社會參考和所有其他參考。

一些只是查找表,並具有廣告ID,描述開始和結束日期以及訂單屬性。

其他(社會,人員和結果)是具有其他屬性和引用的復雜對象(社會具有類型,合同,員工列表,af結構和過程列表;人員具有辦公屬性,知識列表,晉升歷史等;該域包含140多個實體,因此情況要復雜得多,我無法將其全部介紹回來,希望這個例子足夠了)

因此,必須在不引用任何活動的情況下創建所有該對象,但是活動可以引用所有活動

我模型的一小部分

模型

即使是示例代碼,也不是域實體。 這是一個貧乏的領域模型 ,我不知道為什么要測試它,因為應用程序代碼中的任何代碼都會使該模型處於不一致狀態。

測試域實體時,應該測試行為 行為是使用實體中的方法添加的。 最簡單的入門方法是將所有資產設置者設為私有。

public User
{
  Address _address;

  public User(int id, Address address)
  {
     //assign
  }

  public int Id {get;private set;}

  public bool IsAddressSame(Address address)
  {
      if (address == null)
          throw new ArgumentNullException("address");

      return address.Equals(_address);
  }

  public void Move(Address newAddress)
  {
      if (address == null)
          throw new ArgumentNullException("address");

      var old = _address;
      _address = newAddress;
      DomainEvent.Publish(UserMoved(_old, newAddress);
  }  
}

現在,您可以測試地址方法的行為。

如果不創建自己的接口,然后使用moq在測試虛擬對象中創建實現,我如何測試MyClass1與MyClass2隔離呢?

不要為您的域實體創建接口。 接口的目的是創建抽象,以消除應用程序其他部分的依賴關系。 抽象出域模型的目的是什么? 在DDD中,這是您應用程序的核心。 沒有它,你就沒有一個。

由於狀態已被保護,因此您知道每個實體都有正確的狀態。 因此,您無需將用戶與地址隔離。 相反,只需為已創建的方法編寫測試。

更新以回答評論

“當您測試域實體時,您應該測試行為”似乎很好,但是在TDD中,我必須在創建實現之前和之后編寫測試,因此,如果我想要具有Class2屬性的Class1,我首先必須測試我是否具有此屬性

否。在TDD中,您還應該測試行為。 依賴關系由行為驅動,反之亦然。

我的意思是,除非您有口頭要求必須對用戶地址進行某些操作,否則根本無法知道用戶和地址之間是否存在關系。

並根據該要求,您應該在User類上測試該方法,並且不要通過直接訪問地址來違反Demeter法則

通常,在這種情況下,您無法擺脫MyClass1在MyClass2上的依賴性,您必須同時測試它們。 這就是為什么一個類不應該依賴於其他類而應該依賴於接口的原因之一。 您提供的代碼設計不當,因為它違反了SOLID規則之一-依賴項注入。 但是,即使在這種情況下,也希望您的代碼僅針對MyClass1准備單元測試,但您必須滿足一個條件-要使用模擬程序安裝的MyClass2的所有方法和屬性必須聲明為虛擬。 多虧了這個Mock才能用自己的實現覆蓋它們。 否則,您將嘗試對非虛擬方法或屬性使用安裝程序或驗證,最終將導致異常。 要獲取類的模擬,您只需執行與獲取接口的模擬相同的操作:

Mock<MyClass2> mock = new Mock<MyClass2>();
mock.Setup(c => c.Method());

通常,我會說不要僅僅因為在測試中需要它們就創建接口。 創建接口以進行抽象(真正的抽象,而不是發明的抽象)。

至於關於如何測試代碼的問題,取決於它(如果這實際上是代碼),我認為這可能不是您需要的測試:

Aseert.That(mc1.Class2 , Is.EqualTo(moqC2 ));

您在這里測試什么? 自動實現的屬性可以正常工作嗎? 您可能會放棄此測試,因為它的價值很小。 (除非這只是一個示例,否則,您可以發布另一個更真實的示例嗎?)

您的實體和值對象不需要接口。

如果您的示例是真實場景,那么您將需要重新考慮您的設計。 一個集合不應包含對另一個集合的引用。 因此,可以並且應該對聚合進行整體測試。

編輯:

聚集包含沒有根就無法存在的其他對象。 最常見的示例是Order / OrderLine 沒有OrderOrderLine沒有意義。 絕對不會對單獨站立的OrderLine感興趣。

我知道有人會爭辯說,您的Activity如果不與Society相關/不屬於Society就不會存在。 但這就是區別所在。 沒有Customer Order毫無意義。 因此,所有權不決定匯總。

使用“ Order示例可以以多種方式表示該Customer 如果沒有Customer集合/實體,那么它甚至可能是訂單中的價值對象。 例如,這就是處理現金銷售的方式。 但是,引用完整的Customer聚合是有問題的,因為現在從數據存儲中檢索Order會增加如何在其中獲取Customer對象的復雜性,而且也可能不會在那里停止。 那客戶的地址呢? 那么OrderLineProduct產品的PreferredSupplier等等。

DDD的所有方面都是硬邊界到邊界,以實現低耦合。 Order / OrderLine情況下, Customer既可以通過客戶的ID或某個值對象,即使是可能包含如客戶名稱一些反規范化的數據來表示。

在您的情況下,您可以使用Society > Activity而不是Customer > Order

希望有幫助:)

我沒有理由為我的商品創建界面,但是在這一點上我很難創建測試

許多開始測試的人傾向於使用模擬來創建存根/偽造/虛擬實例 它有自己的優點 (可以在一行代碼中創建實例) 和缺點 (它們沒有應指定的行為,有時很難建立正確的行為)。

我在安排測試數據時也遇到了同樣的問題,但是我找到了幾種解決方案。

第一個解決方案是ObjectMother模式

var myClass1 = ClassBuilder.GetClass1();
var myClass2 = ClassBuilder.GetClass2();

方法GetClass1()GetClass2()封裝了創建邏輯。

第二個是測試數據構建器模式

var myClass1 = new Class1Builder.Build();
var myClass2 = new Class2Builder.WithSomeProprty().Build();

Class1BuilderClass2Builder實現了流暢的接口 ,該接口允許我們使用足夠清晰的名稱鏈接方法。

關於Object MotherTest Data Builder模式,有兩篇不錯的老文章。

我沒有使用過,但是有些人使用以下框架來構建測試數據:

暫無
暫無

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

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