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é)