--- tags: Microservice Pattern, Microservice --- # Testing microservices: Part 1 傳統軟體開發進入測試階段後,會交給QA負責,而QA大部分是透過人工方式測試,這樣的測試是很沒效率的: * 人工測試很沒效率 * 僅在軟體開發完成後才測試是不夠的 -> 撰寫階段自動測試 ## 9.1 Testing strategies for microservice architectures > have **automated tests** that you can run **during development**. ex. 設計一鍵測試機制,僅需幾秒就能測試完成: * 如何撰寫測試程式 * 如何拿捏測試細膩度 ### 9.1.1 Overview of testing 測試定義: > A test case is a set of test inputs, execution conditions, and expected results developed for a particular objective, such as to exercise a particular program path or to verify compliance with a specific requirement. ![](https://i.imgur.com/lUtAUnh.jpg) #### WRITING AUTOMATED TESTS Automated tests通常透過testing framework開發(ex. JUnit -> Java testing framework)。 automated test由四個步驟組成: * Setup: 初始化test fixture(包含SUT及其他會用到部分) * Execute: 調用(測試)SUT * Verify: 由調用結果驗證SUT * Teardown: 清除fixture中測試資料 ![](https://i.imgur.com/N0pKi80.jpg) #### TESTING USING MOCKS AND STUBS SUT間常有相依性(dependencies),但各SUT測試複雜度、速度不一,整合測試會造成測試緩慢、誤判測試的結果等問題 -> test an SUT in isolation test double: ![](https://i.imgur.com/e8D8MAs.jpg) test doubles有兩種類型: * stubs: returns values to the SUT * mocks: a test uses to verify that the SUT correctly invokes a dependency > mock is often a stub. [test double介紹1](http://teddy-chen-tw.blogspot.com/2014/09/test-double1.html) [test double介紹2](http://teddy-chen-tw.blogspot.com/2014/09/test-double2.html) #### THE DIFFERENT TYPES OF TESTS > this chapter focus on automated tests that verify the **functional aspects** of the application or service. * Unit tests: Test a **small part** of a service, such as a class. * Integration tests: Verify that a service can interact **with infrastructure services** such as databases and other application services. * Component tests: Acceptance tests for **an individual service**. * End-to-end tests: Acceptance tests for the **entire application**. 依scope區分測試只是其中一種方法,另一種方法為象限測試(quadrant). #### USING THE TEST QUADRANT TO CATEGORIZE TESTS [Brian Marick’s test quadrant](http://www.exampler.com/old-blog/2003/08/21/#agile-testing-project-1): ![](https://i.imgur.com/xkDtDea.jpg) * Q1—Support programming/technology facing: unit and integration tests * Q2—Support programming/business facing: component and end-to-end test * Q3—Critique application/business facing: usability and exploratory testing * Q4—Critique application/technology facing: nonfunctional acceptance tests such as performance tests [Testing Quadrants](https://kojenchieh.pixnet.net/blog/post/75411628) #### USING THE TEST PYRAMID AS A GUIDE TO FOCUSING YOUR TESTING EFFORTS [test pyramid](https://martinfowler.com/bliki/TestPyramid.html): 越下層的測試越簡單、快速,可大量測試,反之,越上層的測試越複雜、慢,無法大量測試。 ![](https://i.imgur.com/aepOqe1.jpg) ### 9.1.2 The challenge of testing microservices IPC對於microservice相當重要,第三章提到很多種IPC溝通方式,在系統上可能會交錯使用。 ![](https://i.imgur.com/SKPcCpF.jpg) > * REST client -> service: The API gateway routes requests to services and implements API composition. > * Domain event consumer -> publisher: Order History Service consumes events published by Order Service. > * Command message requestor -> replier: Order Service sends command messages to various services and consumes the replies. 每個interaction都是兩端的agreement或contract,不應該為了修正一端的bug而改動 -> stable APIs 驗證兩端service時最簡單的方式為直接串起來測試(end-to-end),但可能會需要依賴其他service傳遞(transitive dependencies)。較好的驗證方式為**consumer-driven contract testing** #### CONSUMER-DRIVEN CONTRACT TESTING 在consumer contract test中,通常service間有**consumer-provider relationship**(Ex.API gateway=consumer,Order service=provider) consumer contract test主要是藉由不同consumer來測試provider,且只聚焦於**shape**上。 以REST endpoint為例,consumer contract test僅測試: * Has the expected HTTP method and path * Accepts the expected headers, if any * Accepts a request body, if any * Returns a response with the expected status code, headers, and body ![](https://i.imgur.com/wV0XP6S.jpg) 實務上常用Spring Cloud Contract撰寫consumer contract tests #### TESTING SERVICES USING SPRING CLOUD CONTRACT 值得使用的contract testing framework: [Spring Cloud Contract](https://cloud.spring.io/spring-cloud-contract/reference/html/) [Pact family of frameworks](https://github.com/pact-foundation) Spring Cloud Contract有以下特色: * 提供Groovy domain-specific language(DSL)撰寫contracts * 每個contract都是實體的interaction(Ex. HTTP request and response) * configures mocks(Ex. mock HTTP server) 測試流程如下: ![](https://i.imgur.com/q3Wbvw9.jpg) contract範例: ![](https://i.imgur.com/wQpuJXo.jpg) #### CONSUMER CONTRACT TESTS FOR MESSAGING APIS 除了REST client外,也會有透過asynchronous request/response方式溝通的client,Spring Cloud Contract提供request message與response message來處理request/response內容。 ### 9.1.3 The deployment pipeline 每個service都有deployment pipeline,各步驟理想上是automated,但還是有些步驟是manual的。 deployment pipeline通常使用Continuous Integration (CI) server(Ex. Jenkins): ![](https://i.imgur.com/uUSSFVR.jpg) * Pre-commit tests stage: Runs the unit tests. This is executed by the developer before committing their changes. * Commit tests stage: Compiles the service, runs the unit tests, and performs static code analysis. * Integration tests stage: Runs the integration tests. * Component tests stage: Runs the component tests for the service. * Deploy stage: Deploys the service into production. ## 9.2 Writing unit tests for a service 試想你要測試order service中算錢的功能,可能需要將實際order訂單流程全走一次,到最後才能驗證算錢的功能,這是相當沒效率的。 -> unit test ![](https://i.imgur.com/AU96ORx.jpg) * Solitary unit test: Tests a class in isolation using mock objects for the class’s dependencies * Sociable unit test: Tests a class and its dependencies Controller and service classes通常使用solitary unit tests. Domain objects(entities and value objects) 則通常使用sociable unit tests. ![](https://i.imgur.com/7Qu6HS1.jpg) * Entities, such as Order, which as described in chapter 5 are objects with persistent identity, are tested using sociable unit tests. * Value objects, such as Money, which as described in chapter 5 are objects that are collections of values, are tested using sociable unit tests. * Sagas, such as CreateOrderSaga, which as described in chapter 4 maintain data consistency across services, are tested using sociable unit tests. * Domain services, such as OrderService, which as described in chapter 5 are classes that implement business logic that doesn’t belong in entities or value objects, are tested using solitary unit tests. * Controllers, such as OrderController, which handle HTTP requests, are tested using solitary unit tests. * Inbound and outbound messaging gateways are tested using solitary unit tests. ### 9.2.1 Developing unit tests for entities 以OrderTest class舉例: ![](https://imgur.com/IOHKdrJ.jpg) * @Before: create order * @Test: 初始化並測試order entities屬於sociable unit tests,而Order class relies on the Money value object ### 9.2.2 Writing unit tests for value objects Value objects是不變的(immutable),相當容易測試,不需擔心side effects: ![](https://i.imgur.com/at6jbd4.jpg) ### 9.2.3 Developing unit tests for sagas 測試saga時,除了happy path以外,更要測試各種失敗情境。且必須使用real database與message broker模擬saga participants: ![](https://i.imgur.com/CG4M617.jpg) ![](https://i.imgur.com/TjW1L9o.jpg) ### 9.2.4 Writing unit tests for domain services domain service class(Ex. OrderService),主要功能為調用entities與repositories,並發佈domain events。通常使用solitary unit test,步驟如下: 1. Setup—Configures the mock objects for the service’s dependencies 2. Execute—Invokes a service method 3. Verify—Verifies that the value returned by the service method is correct and that the dependencies have been invoked correctly ![](https://i.imgur.com/9A9MJbK.jpg) ![](https://i.imgur.com/TumVZg6.jpg) ### 9.2.5 Developing unit tests for controllers service可能會依賴來處理HTTP requests(from other services、API gateway)。controller classs內有一系列request handler methods: > 書中採用Rest Assured Mock MVC來模擬controller classs的dependencies ![](https://i.imgur.com/3kw7ARG.jpg) ![](https://i.imgur.com/a7DyG1Z.jpg) ### 9.2.6 Writing unit tests for event and message handlers message handler跟controller很像,負責處理domain events publish,但message handler聚焦於服務間的訊息傳遞: ![](https://i.imgur.com/LW2lZm7.jpg) ### Summary * Automated testing * system under test (SUT):被測試的對象 * 金字塔(pyramid)方式區分測試大小、粗細度... * test doubles(stubs and mocks) * stubs: returns values to the SUT * mocks: a test uses to verify that the SUT * unit測試 * Solitary: domain logic、controller、message adapter * Sociable: entity、value object、Saga