AxonFramework測(cè)試

CQRS最大的好處之一,尤其是事件溯源就事件和命令而言,單純地表達(dá)測(cè)試是可能的。這兩個(gè)功能組件,事件和命令對(duì)領(lǐng)域?qū)<一驑I(yè)務(wù)所有者都有明確的含義。這不僅意味著測(cè)試表達(dá)就事件和命令而言有明確的功能含義,這也意味著他們不依靠任何實(shí)現(xiàn)選擇。

本章描述的特性需要axon-test模塊,可通過配置maven依賴(使用<artifactId>axon-test</artifactId> 和<scope>test</scope)或通過完整包下載。

本章中描述的固件可用于任何測(cè)試框架,如JUnit和TestNG。

命令組件測(cè)試

在任何CQRS基礎(chǔ)架構(gòu)中命令處理組件通常是最復(fù)雜的。比其他組件更復(fù)雜,這也意味著該組件有額外的與測(cè)試相關(guān)的需求。

雖然更復(fù)雜,但是命令的API處理組件相當(dāng)容易。它有一個(gè)命令進(jìn)來,然后事件出去。在某些情況下,可能有一個(gè)查詢作為命令執(zhí)行的一部分。除此之外,命令和事件是API的唯一部分。這意味著可以在事件和命令的基礎(chǔ)上完整地定義測(cè)試場(chǎng)景。典型地,以:
? given過去的某些事件,
? when 執(zhí)行這個(gè)命令,
? expect 這些事件將被發(fā)布和/或存儲(chǔ)

Axon Framework提供了一個(gè)測(cè)試固件,使你能夠做到這一點(diǎn)。AggregateTestFixture允許你配置某些基礎(chǔ)設(shè)施,包括必要的命令處理器和存儲(chǔ)庫,并以given-when-then形式的事件和命令來表達(dá)你的場(chǎng)景。

下面的示例展示了用JUnit 4對(duì)given-when-then測(cè)試固件的使用:

public class MyCommandComponentTest {
 private FixtureConfiguration fixture;
 @Before
 public void setUp() {
     fixture = new AggregateTestFixture(MyAggregate.class);
 }

 @Test
 public void testFirstFixture() {
     fixture.given(new MyEvent(1))
            .when(new TestCommand())
            .expectSuccessfulHandlerExecution()
            .expectEvents(new MyEvent(2));
     /*
     These four lines define the actual scenario and its expected
     result. The first line defines the events that happened in the
     past. These events define the state of the aggregate under test.
     In practical terms, these are the events that the event store
     returns when an aggregate is loaded. The second line defines the
     command that we wish to execute against our system. Finally, we
     have two more methods that define expected behavior. In the
     example, we use the recommended void return type. The last method
     defines that we expect a single event as result of the command
     execution.
     /*
 }
}

given-when-then測(cè)試固件定義了三個(gè)階段:配置、執(zhí)行和驗(yàn)證。每個(gè)階段由不同的接口表示:分別是,F(xiàn)ixtureConfiguration, TestExecutor 和 ResultValidator。固件類的靜態(tài)newGivenWhenThenFixture()方法提供了對(duì)第一個(gè)的引用,進(jìn)而可能提供驗(yàn)證,等等。

注意
為了最好地利用這些階段之間的遷移,最好使用這些方法提供的流式接口,如上面的示例所示。

在配置階段(即在提供第一個(gè)“given”之前),你提供了執(zhí)行測(cè)試所需的構(gòu)件。作為固件的一部分提供事件總線、命令總線和事件存儲(chǔ)的專用版本。有accessor方法來獲得對(duì)它們的引用。任何命令處理器不直接在聚合上注冊(cè),需要顯式地使用registerAnnotatedCommandHandler 方法配置。除了帶注解的命令處理器外,你還可以配置各種組件和設(shè)置,定義應(yīng)該如何設(shè)置測(cè)試周圍的基礎(chǔ)設(shè)施。

一旦固件配置好,你就可以定義“given”事件。測(cè)試固件將用DomainEventMessage包裝這些事件。如果“given”事件實(shí)現(xiàn)消息,消息的有效負(fù)載和元數(shù)據(jù)將被納入DomainEventMessage,否則given事件作為有效負(fù)載。DomainEventMessage 的序列號(hào)順序,從0開始。

或者,你也可以為“given”場(chǎng)景提供命令。在這種情況下,在執(zhí)行實(shí)際的測(cè)試命令時(shí),這些命令生成的事件將被用于事件源聚合。使用“givenCommands(…)”方法提供命令對(duì)象。

執(zhí)行階段允許你提供一個(gè)針對(duì)命令處理組件執(zhí)行的命令。對(duì)調(diào)用處理程序的行為(無論是在聚合或外部處理程序)進(jìn)行監(jiān)控,并與在驗(yàn)證階段注冊(cè)的預(yù)期進(jìn)行比較。

注意
在執(zhí)行測(cè)試過程中,Axon試圖檢測(cè)測(cè)試中的所有在聚合上的非法狀態(tài)的更改。它通過將聚合的狀態(tài)與命令執(zhí)行后的聚合狀態(tài)進(jìn)行比較,如果它從所有“given”和存儲(chǔ)的事件溯源。如果狀態(tài)不相同,這意味著狀態(tài)變化發(fā)生在聚合事件處理器方法之外。比較時(shí)將忽略靜態(tài)和瞬態(tài)字段,因?yàn)樗鼈兺ǔ0瑢?duì)資源的引用。
可以使用setReportIllegalStateChange方法在固件的配置中切換檢測(cè)。

最后一個(gè)階段是驗(yàn)證階段,允許你檢查命令處理組件的活動(dòng)。這完全是根據(jù)返回值和事件來完成的。

測(cè)試固件允許你驗(yàn)證命令處理程序的返回值。你可以顯式地定義預(yù)期的返回值,或者簡(jiǎn)單地要求成功返回該方法。你也可以表達(dá)任何你期望的CommandHandler拋出的異常。

另一個(gè)組件是對(duì)已發(fā)布事件的驗(yàn)證。有兩種匹配預(yù)期事件的方法。

第一是通過事件實(shí)例,它需要與實(shí)際的事件是行逐字的比較。將預(yù)期事件的所有屬性與實(shí)際事件中的對(duì)應(yīng)對(duì)象進(jìn)行比較(使用equals())。如果其中一個(gè)屬性不相等,則測(cè)試失敗,并生成一個(gè)廣泛的錯(cuò)誤報(bào)告。

表達(dá)期望的另一種方式是使用的匹配器(Hamcrest庫提供的)。匹配器接口規(guī)定了兩個(gè)方法matches(Object)和describeTo(Description)。第一個(gè)返回一個(gè)布爾值,指示是否匹配或不匹配。第二個(gè)讓你表達(dá)你的期望。例如,一個(gè)“GreaterThanTwoMatcher”可以添加“任何值大于2的事件“的描述。描述允許創(chuàng)建關(guān)于測(cè)試用例失敗的錯(cuò)誤消息。

創(chuàng)建事件列表的匹配器可能是繁瑣和容易出錯(cuò)的工作。為了簡(jiǎn)化問題,Axon提供了一組匹配器允許你提供一組特定于事件的匹配器,并告訴Axon應(yīng)該如何匹配列表。

下面是可用的事件列表匹配器和他們的目的的概述:

  • List with all of: Matchers.listWithAllOf(event matchers...)
    如果所有的事件匹配器與真實(shí)事件列表中至少一個(gè)事件匹配,該匹配器將成功。不管是否有多個(gè)匹配器匹配相同的事件,或如果列表中一個(gè)事件不匹配任何匹配器。

  • List with any of: Matchers.listWithAnyOf(event matchers...)
    如果一個(gè)或多個(gè)事件匹配器與實(shí)際的事件列表中一個(gè)或多個(gè)事件匹配,該匹配器將成功。一些匹配器甚至一個(gè)也不匹配,而另一個(gè)匹配多個(gè)。

  • Sequence of Events: Matchers.sequenceOf(event matchers...)
    使用此匹配器來驗(yàn)證實(shí)際事件匹配器和提供的事件匹配器有相同的順序。如果匹配器與后一個(gè)事件相匹配,與前一個(gè)匹配器匹配的事件相匹配,該匹配器將成功。這意味著可能出現(xiàn)不匹配事件的“gaps”。
    如果評(píng)估事件之后,更多的匹配器是可用的,他們都是匹配“null”。它是由事件的匹配器來決定是否接受。

  • Exact sequence of Events: Matchers.exactSequenceOf(event matchers...)
    “事件的序列”匹配器的變化不允許不匹配事件的空隙。這意味著每個(gè)匹配器必須與事件后面的事件相匹配,與前一個(gè)匹配器匹配的事件相匹配。每個(gè)匹配器都應(yīng)該與它前一個(gè)匹配器相對(duì)應(yīng)的事件的后續(xù)一個(gè)事件相匹配

為了方便起見,提供了一些普遍需要的事件匹配器。他們與單個(gè)事件實(shí)例相匹配:

  • Equal Event: Matchers.equalTo(instance...)
    驗(yàn)證given對(duì)象在語義上等于given事件,這個(gè)匹配器將比較實(shí)際和預(yù)期的對(duì)象的所有字段的值使用一個(gè)null-safe相等方法。這意味著可以比較事件,即使它們不實(shí)現(xiàn)equals方法。存儲(chǔ)在given參數(shù)字段上的對(duì)象用equals進(jìn)行比較,要求他們正確實(shí)現(xiàn)。

  • No More Events: Matchers.andNoMore() or Matchers.nothing()
    僅與空值匹配,這個(gè)匹配器可以作為最后一個(gè)匹配器添加到事件的準(zhǔn)確順序匹配器,以確保沒有不匹配的事件依然存在。

由于匹配器傳遞一個(gè)事件消息列表,有時(shí)你只是想驗(yàn)證消息的有效負(fù)載。有匹配器來幫助你:

  • Payload Matching: Matchers.messageWithPayload(payload matcher)
    驗(yàn)證消息的有效負(fù)載匹配給定的有效載荷匹配器。

  • Payloads Matching: Matchers.payloadsMatching(list matcher)
    驗(yàn)證消息的有效負(fù)載匹配給定的有效載荷匹配器。給定的匹配器必須匹配列表包含的每個(gè)消息的有效負(fù)載。有效負(fù)載匹配匹配器通常用作外匹配器,以防止重復(fù)有效負(fù)載匹配器。

下面是一個(gè)簡(jiǎn)單的代碼示例,以顯示這些匹配器的使用。在這個(gè)例子中,我們預(yù)期共有兩個(gè)事件發(fā)布。第一個(gè)事件必須是一個(gè)“ThirdEvent”,第二個(gè)是“aFourthEventWithSomeSpecialThings”??赡軟]有第三個(gè)事件,因?yàn)槟菢?andNoMore"匹配器會(huì)失敗。

fixture.given(new FirstEvent(), new SecondEvent())
       .when(new DoSomethingCommand("aggregateId"))
       .expectEventsMatching(exactSequenceOf(
           // we can match against the payload only:
           messageWithPayload(equalTo(new ThirdEvent())),
           // this will match against a Message
           aFourthEventWithSomeSpecialThings(),
           // this will ensure that there are no more events
           andNoMore()
       ));

// or if we prefer to match on payloads only:
       .expectEventsMatching(payloadsMatching(
               exactSequenceOf(
                   // we only have payloads, so we can equalTo directly
                   equalTo(new ThirdEvent()),
                   // now, this matcher matches against the payload too
                   aFourthEventWithSomeSpecialThings(),
                   // this still requires that there is no more events
                   andNoMore()
               )
       ));
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • 文章來自:http://blog.csdn.net/mj813/article/details/52451355 ...
    好大一只鵬閱讀 9,345評(píng)論 2 126
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,253評(píng)論 6 342
  • 作為一株車前草她很是不能理解,什么時(shí)候車前草也能修煉成精了,何況她什么都沒做就莫名的幻成人形了,她覺得很是對(duì)...
    煌箜閱讀 344評(píng)論 10 3
  • 人生已經(jīng)過去半,知道美好的年華老去,不用在曲意逢迎,知道自己責(zé)任,也知道自己愛好,只想做自己的喜歡的有意義的事
    東東少閱讀 199評(píng)論 0 1

友情鏈接更多精彩內(nèi)容