Java & Groovy & Scala & Kotlin - 31.Test

Overview

測試分類

測試過程通常有很多種分類方法山叮,一般常說的有那么幾種: UT计露,IT(CT)博脑,ST,MT票罐。

UT 單元測試屬于白盒測試叉趣,是測試中的最小單元,一般用于測試單獨(dú)的方法该押,檢測方法的正確性疗杉。

IT 集成測試(又可稱為 CT 結(jié)合測試),屬于灰盒測試蚕礼,在單元測試之后進(jìn)行烟具,一般用于測試各個接口之間的數(shù)據(jù)傳遞功能,由數(shù)個單元測試的對象組成奠蹬。

ST 系統(tǒng)測試朝聋,屬于黑盒測試,在集成測試之后進(jìn)行囤躁,用于測試系統(tǒng)與原定于的需求的契合度冀痕。

MT 隨機(jī)測試,屬于黑盒測試狸演,測試要求隨機(jī)言蛇,可以無需知道任何業(yè)務(wù)邏輯,主要用于測試系統(tǒng)的穩(wěn)定性宵距。

測試相關(guān)技術(shù)

TDD

TDD 為測試驅(qū)動開發(fā)腊尚。步驟一般是先按照預(yù)期的調(diào)用方式編寫調(diào)用測試用例,然后做最小修改使測試用例可以跑通满哪,然后繼續(xù)寫下一個測試用例婿斥,所有測試用例跑通后一個可用的類便編寫完成。

TDD 的最大優(yōu)點就是編寫的代碼是可測試的翩瓜,容易調(diào)用的受扳,解決了空想造成的代碼耦合度高携龟,不可測試兔跌,很難調(diào)用的問題。有不少人提倡所有開發(fā)都應(yīng)該使用 TDD 技術(shù)峡蟋,但是 TDD 本身會加大工作量坟桅,有時會為了不必要的解耦增加系統(tǒng)的復(fù)雜性华望。

BDD

BDD 為行為驅(qū)動開發(fā)。其最大的特色是編寫測試用例之前先使用自然語言來編寫該段代碼的目的仅乓,本質(zhì)上是對原來的測試驅(qū)動開發(fā)做了擴(kuò)展赖舟。由于使用了自然語言來進(jìn)行描述,因此非程序員也能進(jìn)行一定程度的閱讀夸楣,而不用等程序員來進(jìn)行翻譯宾抓。

Java

概述

Java 中 Unit Test 主要有這么三個概念:Test Runner,Test Suit豫喧,Test Case石洗。

  • Test Runner 主要用于運(yùn)行測試用例。
  • Test Suit 用于收集 Test Case紧显,是 Test Case 的集合讲衫。
  • Test Case 即測試用例,默認(rèn)的 Test Case 訪問權(quán)限為 public 且方法名開頭為 test孵班。

實現(xiàn) UnitTest

第一種方式是繼承 TestCase 涉兽,該類有兩個特殊方法 setUp()tearDown(),前者在每個測試用例執(zhí)行前執(zhí)行篙程,后者在每個測試用例執(zhí)行完執(zhí)行枷畏。所有以 test 開頭的方法都會被認(rèn)為是測試用例。

例:

public class Test01 extends TestCase {

    private Calculator calculator;

    @Override
    public void setUp() throws Exception {
        super.setUp();
        calculator = new Calculator();
    }

    @Override
    public void tearDown() throws Exception {
        super.tearDown();
        System.out.println("End test...");
    }

    public void testSum() {
        assert 3 == calculator.sum(1, 2);
        assert 3 != calculator.sum(2, 5);
    }

    public void testMock() {
        Calculator calculator = Mockito.mock(Calculator.class);
        Mockito.when(calculator.sum(1, 2)).thenReturn(10);
        assert 10 == calculator.sum(1, 2);
        assert 0 == calculator.sum(1, 20);
    }

    public void testStory() {
        Animal elephant = new Animal("Elephant");
        Animal giraffa = new Animal("Giraffa");
        Refrigerator refrigerator = new Refrigerator();

        refrigerator.putInto(giraffa);
        assert !refrigerator.isEmpty();

        refrigerator.takeOut();
        refrigerator.putInto(elephant);
        assert !refrigerator.isEmpty();

        Animal animal = refrigerator.takeOut();
        assert refrigerator.isEmpty();
        assertEquals(animal, elephant);
    }

    private class Calculator {
        public int sum(int x, int y) {
            return x + y;
        }
    }

    private class Refrigerator {
        private Animal animal;

        public boolean putInto(Animal animal) {
            if (isEmpty()) {
                this.animal = animal;
                return true;
            }
            return false;
        }

        public Animal takeOut() {
            if (isEmpty()) return null;
            Animal temp = animal;
            animal = null;
            return temp;
        }

        public boolean isEmpty() {
            return animal == null;
        }
    }

    private class Animal {
        public String type;

        public Animal(String type) {
            this.type = type;
        }
    }
}

第二種方式是使用注解房午,適合那些無法繼承 TestCase 的類矿辽。標(biāo)為 @Test 的方法會被收集為測試用例,標(biāo)為 @Before 的方法會在每個測試用例執(zhí)行前執(zhí)行郭厌,標(biāo)為 @After 的方法會在每個測試用例后執(zhí)行袋倔。

例:

public class Test02 {

    private Calculator calculator;

    @Before
    public void setUp() {
        calculator = new Calculator();
    }

    @After
    public void tearDown() {
        System.out.println("End test...");
    }

    @Test
    public void testSum() {
        assert 3 == calculator.sum(1, 2);
        assert 3 != calculator.sum(2, 5);
    }

    @Test
    public void testMock() {
        Calculator calculator = Mockito.mock(Calculator.class);
        Mockito.when(calculator.sum(1, 2)).thenReturn(10);
        assert 10 == calculator.sum(1, 2);
        assert 0 == calculator.sum(1, 20);
    }

    private class Calculator {
        public int sum(int x, int y) {
            return x + y;
        }
    }
}

Groovy

實現(xiàn) UnitTest

繼承 GroovyTestCase

例:

class Test01 extends GroovyTestCase {

    private Calculator calculator

    @Override
    protected void setUp() throws Exception {
        super.setUp()
        calculator = new Calculator()
    }

    @Override
    protected void tearDown() throws Exception {
        super.tearDown()
        println("End test...")
    }

    def testSum() {
        assert 3 == calculator.sum(1, 2)
        assert 3 != calculator.sum(2, 5)
    }

    def testMock() {
        Calculator calculator = Mockito.mock(Calculator.class)
        Mockito.when(calculator.sum(1, 2)).thenReturn(10)
        assert 10 == calculator.sum(1, 2)
        assert 0 == calculator.sum(1, 20)
    }

    def testStory() {
        Animal elephant = new Animal(type: "Elephant")
        Animal giraffa = new Animal(type: "Giraffa")
        Refrigerator refrigerator = new Refrigerator()

        refrigerator.putInto(giraffa)
        assert !refrigerator.isEmpty()

        refrigerator.takeOut()
        refrigerator.putInto(elephant)
        assert !refrigerator.isEmpty()

        Animal animal = refrigerator.takeOut()
        assert refrigerator.isEmpty()
        assertEquals(animal, elephant)
    }

    class Calculator {
        def sum(x, y) {
            x + y
        }
    }

    class Refrigerator {
        private Animal animal

        def putInto(Animal animal) {
            if (isEmpty()) {
                this.animal = animal
                true
            }
            false
        }

        def Animal takeOut() {
            if (isEmpty()) return null
            def temp = animal
            animal = null
            temp
        }

        def isEmpty() {
            animal == null
        }
    }

    class Animal {
        public String type
    }
}

Groovy 也能使用注解實現(xiàn)測試用例,但是使用注解時必須使用靜態(tài)聲明折柠,沒有繼承方便宾娜。

Scala

實現(xiàn) UnitTest

例:

class Test01 extends TestCase {
  private var calculator: Calculator = null

  override def setUp() {
    calculator = new Calculator()
  }

  override def tearDown() {
    println("End test...")
  }

  def testSum() {
    assert(3 == calculator.sum(1, 2))
    assert(3 != calculator.sum(2, 5))
  }

  def testMock() {
    val calculator: Calculator = Mockito.mock(classOf[Calculator])
    Mockito.when(calculator.sum(1, 2)).thenReturn(10)
    assert(10 == calculator.sum(1, 2))
    assert(0 == calculator.sum(1, 20))
  }

  def testStory() {
    val elephant: Animal = new Animal("Elephant")
    val giraffa: Animal = new Animal("Giraffa")
    val refrigerator: Refrigerator = new Refrigerator
    refrigerator.putInto(giraffa)
    assert(!refrigerator.isEmpty)
    refrigerator.takeOut
    refrigerator.putInto(elephant)
    assert(!refrigerator.isEmpty)
    val animal: Animal = refrigerator.takeOut
    assert(refrigerator.isEmpty)
    assert(animal == elephant)
  }
}

class Calculator {
  def sum(x: Int, y: Int): Int = x + y
}

class Refrigerator {
  private var animal: Animal = null

  def putInto(animal: Animal): Boolean = {
    if (isEmpty) {
      this.animal = animal
      true
    } else {
      false
    }
  }

  def takeOut: Animal = {
    if (isEmpty) {
      null
    } else {
      val temp = animal
      animal = null
      temp
    }
  }

  def isEmpty: Boolean = {
    animal == null
  }
}

class Animal(val `type`: String)

實現(xiàn) ScalaTest

Scala Test 是 Scala 的一個測試框架,比起直接使用 JUnit 能夠?qū)懗龈鼉?yōu)美的代碼扇售。

例:

@RunWith(classOf[JUnitRunner])
class ScalaTest01 extends FunSuite with BeforeAndAfter {

  private var calculator: Calculator = null

  before {
    calculator = new Calculator
  }

  after {
    println("End test...")
  }

  test("Sum") {
    assert(3 == calculator.sum(1, 2))
    assert(3 != calculator.sum(2, 5))
  }

  test("Story") {
    val elephant: Animal = new Animal("Elephant")
    val giraffa: Animal = new Animal("Giraffa")
    val refrigerator: Refrigerator = new Refrigerator
    refrigerator.putInto(giraffa)
    assert(!refrigerator.isEmpty)
    refrigerator.takeOut
    refrigerator.putInto(elephant)
    assert(!refrigerator.isEmpty)
    val animal: Animal = refrigerator.takeOut
    assert(refrigerator.isEmpty)
    assert(animal == elephant)
  }
}

實現(xiàn) Spec2

Spec2 是另一個 Scala 的測試框架前塔,使用它可以簡單的完成 BDD 的測試用例的編寫。

例:

class SpecTest extends Specification with BeforeAfterEach with Mockito {

    private var calculator: Calculator = null
    override protected def after: Any = {
        println("End test...")
    }
    override protected def before: Any = {
        calculator = new Calculator
    }

    "Arithmetic" should {
        "sum" in {
            "two numbers" in {
                calculator.sum(1, 2) mustEqual 3
                calculator.sum(2, 5) mustNotEqual 3
            }
        }
    }

    "TestMock" should {
        "mock sum method" in {
            "two numbers" in {
                val calculator = mock[Calculator]
                calculator.sum(1, 2) returns 10
                calculator.sum(1, 2) mustEqual 10
                calculator.sum(1, 20) mustEqual 0
            }
        }
    }

    "Story" should {
        val elephant: Animal = new Animal("Elephant")
        val giraffa: Animal = new Animal("Giraffa")
        val refrigerator: Refrigerator = new Refrigerator

        """
           1. put giraffa
           2. takeout giraffa
           3. put elephant
           4. takeout elephant
           the refrigerator should be empty""" in {
            refrigerator.putInto(giraffa)
            refrigerator.isEmpty mustEqual false

            refrigerator.takeOut
            refrigerator.isEmpty mustEqual true

            refrigerator.putInto(elephant)
            val animal: Animal = refrigerator.takeOut
            refrigerator.isEmpty mustEqual true
            animal mustEqual elephant
        }

    }
}

Kotlin

實現(xiàn) UnitTest

第一種方式承冰,繼承 TestCase

例:

class Test01 : TestCase() {

    private var calculator: Calculator? = null

    override fun setUp() {
        super.setUp()
        calculator = Calculator()
    }

    override fun tearDown() {
        super.tearDown()
        println("End test...")
    }

    fun testSum() {
        assert(3 == calculator?.sum(1, 2))
        assert(3 != calculator?.sum(2, 5))
    }

    fun testMock() {
        val calculator = Mockito.mock<Calculator>(Calculator::class.java)
        Mockito.`when`<Int>(calculator.sum(1, 2)).thenReturn(10)
        assert(10 == calculator.sum(1, 2))
        assert(0 == calculator.sum(1, 20))
    }

    fun testStory() {
        val elephant = Animal("Elephant")
        val giraffa = Animal("Giraffa")
        val refrigerator: Refrigerator = Refrigerator()

        refrigerator.putInto(giraffa)
        assert(!refrigerator.isEmpty())

        refrigerator.takeOut()
        refrigerator.putInto(elephant)
        assert(!refrigerator.isEmpty())

        val animal = refrigerator.takeOut()
        assert(refrigerator.isEmpty())
        assertEquals(animal, elephant)
    }

}

//  Declared open when using mock
open class Calculator {
    open fun sum(x: Int, y: Int): Int {
        return x + y
    }
}

class Refrigerator {
    private var animal: Animal? = null

    fun putInto(animal: Animal): Boolean {
        if (isEmpty()) {
            this.animal = animal
            return true
        }
        return false
    }

    fun takeOut(): Animal? {
        if (isEmpty()) return null
        val temp = animal
        animal = null
        return temp
    }

    fun isEmpty(): Boolean {
        return animal == null
    }
}

class Animal(type: String)

第二種方式华弓,使用注解

例:

class Test02 {

    private var calculator: Calculator? = null

    @Before
    fun setUp() {
        calculator = Calculator()
    }

    @After
    fun tearDown() {
        println("End test...")
    }

    @Test
    fun testSum() {
        assert(3 == calculator?.sum(1, 2))
        assert(3 != calculator?.sum(2, 5))
    }

    @Test
    fun testMock() {
        val calculator = Mockito.mock<Calculator>(Calculator::class.java)
        Mockito.`when`<Int>(calculator.sum(1, 2)).thenReturn(10)
        assert(10 == calculator.sum(1, 2))
        assert(0 == calculator.sum(1, 20))
    }

    @Test
    fun testStory() {
        val elephant = Animal("Elephant")
        val giraffa = Animal("Giraffa")
        val refrigerator: Refrigerator = Refrigerator()

        refrigerator.putInto(giraffa)
        assert(!refrigerator.isEmpty())

        refrigerator.takeOut()
        refrigerator.putInto(elephant)
        assert(!refrigerator.isEmpty())

        val animal = refrigerator.takeOut()
        assert(refrigerator.isEmpty())
        assertEquals(animal, elephant)
    }

}

其它測試框架

Kotlin 還有一種測試框架名為 Spek,同 Scala 的 Specs2 一樣困乒,也可以用于進(jìn)行 BDD 測試寂屏,不過目前其還只是 0.1.x 版本,還沒有達(dá)到可以放心用在生成環(huán)境的時候,這里就不介紹了迁霎,有興趣可以訪問 官網(wǎng) 了解詳情吱抚。

Summary

  • JUnit 是所有測試的核心框架。
  • 一般編寫測試用例可以使用繼承和注解兩種方式考廉,繼承的代碼量更少秘豹,注解更加靈活。
  • Scala 除了可以直接使用 JUnit昌粤,也可以使用 scalaTest 和 Specs2 進(jìn)行測試既绕。

文章源碼見 https://github.com/SidneyXu/JGSK 倉庫的 _31_test 小節(jié)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市涮坐,隨后出現(xiàn)的幾起案子岸更,更是在濱河造成了極大的恐慌,老刑警劉巖膊升,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怎炊,死亡現(xiàn)場離奇詭異,居然都是意外死亡廓译,警方通過查閱死者的電腦和手機(jī)评肆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來非区,“玉大人瓜挽,你說我怎么就攤上這事≌鞒瘢” “怎么了久橙?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長管怠。 經(jīng)常有香客問我淆衷,道長,這世上最難降的妖魔是什么渤弛? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任祝拯,我火速辦了婚禮,結(jié)果婚禮上她肯,老公的妹妹穿的比我還像新娘佳头。我一直安慰自己,他們只是感情好晴氨,可當(dāng)我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布康嘉。 她就那樣靜靜地躺著,像睡著了一般籽前。 火紅的嫁衣襯著肌膚如雪亭珍。 梳的紋絲不亂的頭發(fā)上腊瑟,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天,我揣著相機(jī)與錄音块蚌,去河邊找鬼。 笑死膘格,一個胖子當(dāng)著我的面吹牛峭范,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瘪贱,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼纱控,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了菜秦?” 一聲冷哼從身側(cè)響起甜害,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎球昨,沒想到半個月后尔店,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡主慰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年嚣州,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片共螺。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡该肴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出藐不,到底是詐尸還是另有隱情匀哄,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布雏蛮,位于F島的核電站涎嚼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏挑秉。R本人自食惡果不足惜铸抑,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望衷模。 院中可真熱鬧鹊汛,春花似錦、人聲如沸阱冶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽木蹬。三九已至至耻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背尘颓。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工走触, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人疤苹。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓互广,卻偏偏與公主長得像,于是被迫代替她去往敵國和親卧土。 傳聞我的和親對象是個殘疾皇子惫皱,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容