iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 25
0
Software Development

用30天介紹 open source 專案 Ohara 系列 第 25 篇

Day 25 關於 Ohara 的測試程式 (一)

  • 分享至 

  • xImage
  •  

Ohara 在開發時很要求寫測試程式,主要的目的是因為 Ohara 是 Open Source 所以開發者的人數很多,所以不可能一個人就了解所有模組每一段程式的邏輯。因此就有可能不小心改到一段程式碼而影響到其它地方不能執行,最簡單的例子是修改了 Configurator Restful API 傳入參數的資料格式,如果前端沒有撰寫測試程式就會等到使用者在操作 UI 時才會發現程式無法運作,這時前端的開發者就會開始追蹤程式碼,等追到最後才發現 API 傳入的參數有修改。 這個問題如果一開始有撰寫測試可以很快的發現問題並解決,可以減少很多成本的花費,以下列出幾個寫測試程式的好處:

  • 減少一些低級的失誤,例如是程式邏輯寫錯,可以很快的發現問題

  • 程式在重構的時侯,修改起來比較有信心。如果沒有測試程式有很多地方都不敢改

  • 如果發現程式有 bug 可以再增加測試情境,調整程式的邏輯

  • 程式換其它人開發時,如果不了解程式的邏輯也可以先閱讀測試程式,並且可以使用測試程式在 IDE 上執行 Debug mode 來追蹤程式碼

以上就是撰寫測試程式可以得到的幾個好處,雖然寫測試程式有可能會影響到開發的進度,但是這是值得的投資。沒有寫測試程式當系統愈做愈大時所花費的成本會更多,而且程式裡面可能會隱藏許多沒被發現的 Bug,到最後程式可能就不太能修改,加一個功能可能會花費很長的時間。

以下就來簡單的介紹有關於 Ohara 測試程式的部份,Ohara 的測試分為單元測試以及整合測試。今天就來說明單元測試的部份:

單元測試主要用來測試程式的邏輯是否正確,以下舉一段 Ohara 單元測試程式的 sample:

  @Test 
  public void testTags() { 
    Row row = Row.of(Arrays.asList("tag", "tag2"), Cell.of("aa", "aa"), Cell.of("b", 123)); 
    Assert.assertEquals(2, row.tags().size()); 
    Assert.assertEquals("tag", row.tags().get(0)); 
    Assert.assertEquals("tag2", row.tags().get(1)); 
  } 

以上的測試程式主要用來測試 Row 的 tag list 的數量是否正確以及每一個元素內的值是否正確,單元測試主要測試的就是 output 出來的值是否有符合預期的結果,如果沒有符合預期的結果就代表程式邏輯要再去做確認和修改。

在寫測試程式時呼叫方法,傳入的參數如果要求是一個介面 (interface) 或是類別 (class),這時介面的實作或是類別有可能是其它開發者在做,有可能還沒做好或是要避免測試程式的耦合性,不用太關心實作的內容,這時可以使用 mock 的 library 或是建立 Fake 的 class,舉例如下:

Ohara 開發使用的 mock library 是 mockito,版本為 1.10.19,以下是 Ohara 測試程式使用 mock 的一段 sample

  @Test(expected = IllegalArgumentException.class) 
  public void emptyConfigInfos() { 
    ConfigInfos infos = Mockito.mock(ConfigInfos.class); 
    Mockito.when(infos.values()).thenReturn(Collections.emptyList()); 
    SettingInfo.of(infos); 
  } 

在這裡主要會去 mock ConfigInfos 類別,並且當呼叫到 infos 的 values 方法時,要求回傳值為空的 List,之後再把 infos 的變數傳給 SettingInfo.of 測試。測試程式預期當 SettingInfo.of 收到空的 list 時會收到 IllegalArgumentException 的 Exception

有關於 SettingInfo 的程式可以參考以下的連結:
https://github.com/oharastream/ohara/blob/master/ohara-kafka/src/main/java/com/island/ohara/kafka/connector/json/SettingInfo.java

以下是建立 Fake class 測試的 sample code:

  @Test 
  def testBkCreatorZKNotExists(): Unit = { 
    val node1Name = "node1" 
    val node1 = node(node1Name) 
    val brokerCollie = new FakeBrokerCollie(Seq(node1), Seq.empty, Seq.empty) 

    val bkCreator: Future[BrokerClusterInfo] = brokerCollie.creator 
      .clusterName("cluster123") 
      .imageName(BrokerApi.IMAGE_NAME_DEFAULT) 
      .zookeeperClusterName("zk123456") 
      .clientPort(9092) 
      .exporterPort(9093) 
      .jmxPort(9094) 
      .nodeName(node1Name) 
      .create() 

    an[NoSuchClusterException] should be thrownBy { 
      Await.result(bkCreator, TIMEOUT) 
    } 
  } 

以上的測試程式主要用來測試在建立 Broker cluster 時,如果沒有建立 zk123456 的 Zookeeper Cluster 會收到 NoSuchClusterException,在這裡 FakeBrokerCollie 會繼承 BrokerCollie,creator 的程式邏輯會寫到 BrokerCollie 的 trait ,這樣就可以使用 FakeBrokerCollie 去測試 creator 方法的邏輯。不用等到 K8SBrokerCollieImpl 或是 ssh 版本的 BrokerCollieImpl 實作完成,再去做寫測試的部份。 FakeBrokerCollie 的好處就是不用 Kubernetes 和 SSH 的環境也可以執行程式的邏輯測試。

有了 mock 以及建立 Fake class 的測試還是不夠的,因為在 Connector 的執行,是多執行緒在執行,所以有可能我們使用 mock 或是 Fake 的測試邏輯沒問題,但是在部署到真實的 Worker cluster 環境上執行確發現資料筆數,跟我們預期的不一樣。 因此 Ohara 提供在測試程式裡面執行 MiniCluster 的測試方式,確保 connector 程式的執行結果是正確的,以下是建立 MiniCluster 程式簡單的寫法範例:

    val brokerNumber = 1 
    val workerNumber = 1 
    val zookeeper = Zookeepers.local(0) 
    val brokers = Brokers.local(zookeeper, (1 to brokerNumber).map(x => 0).toArray) 
    val workers = Workers.local(brokers, (1 to workerNumber).map(x => 0).toArray) 

    val workerClient = WorkerClient(workers.connectionProps()) 

以上的寫法可以設定 Zookeeper、Broker 和 Worker cluster 的數量,如果數量設定太大,硬體 (CPU、記憶體) 不夠強的話有可能測試程式會發生 timeout exception。

啟動了 cluster 之後就可以把 worker 的連線資訊設定給 WorkerClient,這樣就可以在測試程式上執行 Connector 的測試了。

完整的 connector 測試寫法可以參考 Day 20 和 Day 23

今天已經介紹了有關於 Ohara 單元測試的部份了,明天會再繼續說明有關於整合測試的部份。


上一篇
Day 24 部署自定義的 Sink Connector
下一篇
Day 26 關於 Ohara 的測試程式 (二)
系列文
用30天介紹 open source 專案 Ohara 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言