<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      單元測試的思考與實踐

      1. 什么是單元測試

      通常來說單元測試,是一種自動化測試,同時包含一下特性:

      • 驗證很小的一段代碼(業務意義 或者 代碼邏輯 上不可再分割的單元),能夠更準確的定位到問題代碼的位置
      • 能夠快速運行(單元測試的意義,在于快速且周期性的驗證原有代碼的準確性),提高項目開發效率
      • 以隔離的方式 (isolated manner)運行(對外部依賴通過插樁解耦,避免單元測試的復雜度,實現問題快速定位,簡化單元測試的運行環境,多個單元測試可以以任何順序甚至并行進行)

      2. 為什么要單元測試

      因為單元測試有如下優點:

      1. 能快速的回歸,提高自測的效率
      2. 集成測試或者端到端的手工測試效率低,而且無法覆蓋到更細節的邏輯分支
      3. 也存在功能設計超前于產品設計,通過接口維度,無法觸達某些邏輯分支,需要通過單元測試來覆蓋
      4. 功能開發人員更了解代碼的實現,開發人員寫出的測試用例往往能更全面的覆蓋代碼
      5. 有良好單測的代碼,往往更方便重構
      6. 單元測試是項目代碼的一部分,維護方便,當然這也依賴良好的單元測試編寫習慣,合適的顆粒度

      3. 如何識別有測試價值的代碼?

      當我們考慮給代碼添加 單元測試時,需要首先考慮加入單測后能夠帶來的收益有多少,以及其付出的成本有多少,用最小的維護成本提供最高的價值的單元測試

      3.1 項目屬性
      軟件本身發布更新成本比較大,如嵌入式軟件,客戶端程序;或者 軟件的缺陷 更可能帶來較大的資損,如工廠,銀行內部的軟件,這類軟件都是需要優先考慮單元測試。
      如果一個項目本身不是特別核心的項目,影響面小,迭代更新相對較容易,那么對單元測試的要求,或者說對質量的要求,也就沒有那么強烈

      3.2 代碼屬性
      3.2.1 重要的代碼

      1. 領域層
      2. 基礎設施代碼
        3.2.2 不容易被集成測試覆蓋的代碼
      3. 邊界條件
      4. 異常條件
      5. 低概率場景
        3.2.3 容易出現問題的代碼
      6. 復雜的業務邏輯分支
      7. 狀態機
      8. 膠水代碼:負責組合多個功能,多個功能的輸入具有不確定性

      3.3 個人不建議的單元測試的行為

      1. 通常來說不建議在單元測試的時候,啟動spring容器后,會牽扯過多的外部依賴,導致單元測試難以進行,或者成本過高
      2. 同樣,外部接口,數據庫依賴,中間件依賴,都不建議在單元測試中加載,可以通過mock或者sub的方式來進行隔離

      4. 編寫 Unit Test

      通常按照單元測試的AAA模式來編寫單元測試,分為三部分:Arrange, Act, Assert

      1. Arrange
        準備測試數據和測試環境,確保測試的可重復性和可預測性。這包括初始化對象、設置變量、模擬外部依賴等
      2. Act
        執行實際的測試操作,也就是調用需要測試的方法或函數,并獲取返回值或狀態。這個階段應該僅包含單個操作,以確保測試的獨立性和可維護性
      3. Assert
        驗證測試結果是否符合預期,也就是檢查實際的輸出是否與預期的輸出相同。如果結果不符合預期,我們需要檢查測試代碼和被測試代碼,找出問題所在并進行修復
      4. 結果驗證 - 對函數返回結果進行驗證
      5. 狀態驗證 - 對過程中的屬性值來進行驗證
      6. 行為驗證 - 對過程中會執行的動作進行驗證

      spock測試框架代碼示例:

      
      class OrderServiceImplTest extends Specification {
      
          OrderService orderService = new OrderServiceImpl();
          InventoryService inventoryService = Mock(InventoryService)
          OrderConverter orderConverter = Mock(OrderConverter.class)
          PaymentChannelClient paymentChannelClient = Mock(PaymentChannelClient)
          OrderMapper orderMapper = Mock(OrderMapper)
      
      
          def setupSpec() {}    // runs once -  before the first feature method
          def setup() { // runs before every feature method
              orderService.setInventoryService(inventoryService)
              orderService.setPaymentChannelClient(paymentChannelClient)
              orderService.setOrderMapper(orderMapper)
              orderService.setOrderConverter(orderConverter)
          }
      
          def cleanup() {}      // runs after every feature method
          def cleanupSpec() {}  // runs once -  after the last feature method
      
          def "create order correctly"() {
          
              //準備測試需要的參數
              given:
              Long id = 1
              CreateOrderCommand command = new CreateOrderCommand(orderNo, itemNo, orderItemQuantity, user, totalPrice)
          
              //創建一個spy,可以用來做行為驗證
              MockOrderEntity spyOrder = Spy(constructorArgs: [id, orderNo, itemNo, orderItemQuantity, null, user, totalPrice])
          
              //指定返回spy
              orderConverter.toEntity(_ as CreateOrderCommand) >> spyOrder
          
              LockInventoryCommand lockInventoryCommand = new LockInventoryCommand(itemNo, orderItemQuantity)
          
              when:
              //觸發測試
              Long resultId = orderService.createOrder(command)
          
              then:
              //行為驗證, 創建訂單的同時,執行鎖定庫存lockInventory會被執行一次,同時會驗證參數是否和我們提供lockInventoryCommand是否equals
              1 * inventoryService.lockInventory(lockInventoryCommand)
              //行為驗證,最終訂單執行insert
              1 * spyOrder.insert()
              //結果驗證,驗證返回的id
              resultId == id
              //狀態驗證
              spyOrder.orderStatus == OrderStatus.CREATE
          
              //以表格的形式提供測試數據集合
              where:
              orderNo | itemNo | orderItemQuantity | user    | totalPrice
              "1"     | "it"   | 10                | "userA" | 9.9
          }
          
        }
      
      

      5. 如何自動化執行單元測試

      使用spock框架進行單測,可以通過添加maven插件,來在maven打包的時候自動執行單元測試代碼

      <dependency>
          <groupId>org.spockframework</groupId>
          <artifactId>spock-core</artifactId>
          <version>2.1-groovy-3.0</version>
          <scope>test</scope>
      </dependency>
      <dependency>
          <groupId>net.bytebuddy</groupId>
          <artifactId>byte-buddy</artifactId>
          <version>1.10.19</version>
          <scope>test</scope>
      </dependency>
      
      
      <!-- Mandatory plugins for using Spock -->
      <plugin>
          <groupId>org.codehaus.gmavenplus</groupId>
          <artifactId>gmavenplus-plugin</artifactId>
          <version>1.12.0</version>
          <executions>
              <execution>
                  <goals>
                      <goal>compile</goal>
                      <goal>compileTests</goal>
                  </goals>
              </execution>
          </executions>
      </plugin>
      <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>3.0.0-M5</version>
          <configuration>
              <includes>
                  <!-- 指定后綴為Test的文件,需要被執行單元測試 -->
                  <include>**/*Test</include>
              </includes>
          </configuration>
      </plugin>
      

      6. Spock測試框架中Mock,Stub,Spy的區別

      1. Stub(樁對象):Stub對象用于模擬被測試對象的某些行為。Stub對象通常用來模擬一些外部依賴(interface)返回指定數據,以便于進行單元測試。不能用于用來做行為驗證。
      def ""() {
          given:
          def inventoryMapper = Stub(InventoryMapper)
          InventoryServce inventoryService = new InventoryServiceImpl(inventoryMapper)
          Inventory inv = new Inventory(10)
          inventoryMapper.selectById(_) >> inv
      
          when:
          //inventoryService.stockOut(quantity, id)
          inventoryService.stockOut(5, 1)
      
          then:
          inv.quantity == 5
          
      }
      
      1. Mock(模擬對象):Mock對象和Stub對象類似,但是可以用來做行為驗證,所以在spock中通常可以用mock替代stub
      def ""() {
          given:
          def inventoryMapper = Mock(InventoryMapper)
          InventoryServce inventoryService = new InventoryServiceImpl(inventoryMapper)
          Inventory inv = new Inventory(10)
          inventoryMapper.selectById(_) >> inv
      
          when:
          //inventoryService.stockOut(quantity, id)
          inventoryService.stockOut(5, 1)
      
          then:
          //行為驗證,inventoryMapper執行了一次stockOut
          1 * inventoryMapper.stockOut(_)
          inv.quantity == 5
          
      }
      
      3. Spy(監視對象):上面的Stub,Mock都是創建一個假的實例,而Spy是在真實實例的基礎上,類似創建一個包裝類,它可以記錄被測試對象的行為。既保留了原有實例功能的同時,還可以做行為驗證
      ```groovy
      def ""() {
          given:
          def inventoryMapper = Stub(InventoryMapper)
          InventoryServce inventoryService = new InventoryServiceImpl(inventoryMapper)
          Inventory inv = Spy(Inventory)
          inv.setQuantity(10)
          inventoryMapper.selectById(_) >> inv
      
          when:
          //inventoryService.stockOut(quantity, id)
          inventoryService.stockOut(5, 1)
      
          then:
          //行為驗證,inv執行了一次stockOut
          1 * inv.stockOut(_)
          inv.quantity == 5
          
      }
      

      通常來說調用Spy對象的方法,會被默認委托給真實的對象來執行,即執行真實的方法,但是Spy同樣也適用Stub行為,如:

      def subscriber = Spy(SubscriberImpl, constructorArgs: ["Fred"])
      //Spy對象也可以像 Stub對象一樣,替換掉receive方法,返回指定的值
      subscriber.receive(_) >> "ok"
      
      

      7. Partial Mocks(部分Mock)

      7.1 callRealMethod
      通常來說Mock可以對class或者interface創建一個fake對象,不會執行真實的方法,當在寫單元測試時有時會需要執行Mock對象的某些真實方法的時候,可以callRealMethod的方式來執行。

      given:
      def subscriber = Mock(SubscriberImpl)
      //mock call方法
      subscriber.call(_) >> {return "called"}
      //通過callRealMethod指定mock對象執行原來的真實方法
      subscriber.receive(_) >> { callRealMethod() }
      
      then:
      subscriber.receive("")
      

      7.2 spy
      通過callRealMethod是一種方式,另一種,就是通過Spy來實現,因為Spy是基于真實的對象創建的,那么就可以反過來實現一個對象既可以調用真實方法,又可以調用假的方法

      given:
      def subscriber = Spy(SubscriberImpl, constructorArgs: ["Fred"])
      //mock call方法
      subscriber.call(_) >> {return "called"}
      
      then:
      //這里會直接執行真實方法
      subscriber.receive("")
      

      posted on 2024-04-03 16:32  mindSucker  閱讀(70)  評論(0)    收藏  舉報

      主站蜘蛛池模板: 精精国产XXX在线观看| 一区二区三区av在线观看| 亚洲av第一区二区三区| 蜜桃视频在线免费观看一区二区| 四虎成人精品在永久免费| 人妻中文字幕精品系列| 无码人妻斩一区二区三区| 99久久精品国产一区二区| 天堂在线中文| 美国又粗又长久久性黄大片| 岛国岛国免费v片在线观看| 欧洲中文字幕国产精品| 久久96热在精品国产高清| 亚洲影院丰满少妇中文字幕无码| 亚洲一区二区av观看| 日本精品极品视频在线| 精品无码老熟妇magnet| 免费看国产曰批40分钟| 成人国产亚洲精品一区二区| 55大东北熟女啪啪嗷嗷叫| 亚洲人成网线在线播放VA| 国内外成人综合免费视频| 在线天堂最新版资源| 免费人成在线观看网站| 97免费人妻在线视频| 精品国产午夜福利伦理片| 国产目拍亚洲精品二区| 亚洲欧美中文日韩V日本| 男女做aj视频免费的网站| 国产日韩精品一区在线不卡| 四虎国产精品免费久久| 国产尤物精品自在拍视频首页| 无码日韩做暖暖大全免费不卡| 久久香蕉欧美精品| 国产av永久无码天堂影院| 国产精品人妻熟女男人的天堂 | 内射极品少妇xxxxxhd| 亚洲国产aⅴ成人精品无吗| 加勒比无码人妻东京热| 18av千部影片| 亚洲欧美中文字幕5发布|