軟件測試是 執行的軟件以驗證代碼狀態(state testing)或事件序列(behavior testing)符合預期 。
html
軟件單元測試幫助開發人員驗證程序的部分邏輯是正確的。
java
被測試的代碼一般稱被測代碼。若是您正在測試的應用程序,則爲被測試應用程序。
python
測試夾具是代碼的固定狀態,一般爲測試輸入,也能夠爲預置條件。
android
單元測試用代碼測試代碼,關注狀態和行爲測試。單元測試所測試的百分比一般被稱爲測試覆蓋率。
單元測試一般針對一小段代碼,例如方法或類。經過測試實現或mock排除外部依賴。
單元測試一般不適合測試複雜用戶界面或組件集成。
git
集成測試關注測試組件的行爲或一組組件之間的集成。功能測試有時等同於集成測試。一般須要把user story轉化爲test suite。github
性能測試使用基準重複測試軟件組件。其目的是爲了確保測試的代碼即便是在高負載下也運行速度夠快。
web
行爲測試(也叫交互做用測試)不驗證的方法調用的結果,但檢查方法是否爲用正確的輸入參數調用。狀態測試驗證的結果。
若是您正在測試的算法和系統的功能,你想測試在大多數狀況下的狀態,而不是交互。典型的測試裝置使用嘲笑或相關類的存根,以抽象與這些其餘類路程,測試狀態中的對象,被測試的相互做用。
算法
測試代碼與實際代碼分開。簡單的getter和setter之類代碼一般不須要測試。JVM相關內容一般能夠認爲是可靠的。對已有代碼的測試,一般從出錯最多的地方開始。Java的主流測試框架是JUnit和TestNG。前者用戶最多,對mock框架兼容好。後者可讀性更好。
數據庫
JUnit的4.x版使用註解來指定測試。JUnit的主頁:http://junit.org/,代碼託管:https://github.com/junit-team/junit。JUnit測試是類中用於測試的方法。使用註解@org.junit.Test。方法中使用的斷言方法(JUnit或其餘斷言框架提供),檢查代碼的執行的實際結果與預期。
下面的代碼演示JUnit測試。數組
建立java工程first,並在src目錄下建立test目錄。
建立類MyClass:
package first; public class MyClass { public int multiply(int x, int y) { // the following is just an example if (x > 999) { throw new IllegalArgumentException("X should be less than 1000"); } return x / y; } }
建立測試類MyClassTest,建立方法參見下面的安裝配置部分。
package first; import static org.junit.Assert.assertEquals; import org.junit.Test; public class MyClassTest { @Test(expected = IllegalArgumentException.class) public void testExceptionIsThrown() { MyClass tester = new MyClass(); tester.multiply(1000, 5); } @Test public void testMultiply() { MyClass tester = new MyClass(); assertEquals("10 x 5 must be 50", 50, tester.multiply(10, 5)); } }
選中測試類, 右鍵選擇Run-As → JUnit Test.
在普遍使用的Junit命名方法是類名稱下測試和「測試」加Test後綴。should經常使用於測試方法名,如ordersShouldBeCreated,menuShouldGetActive,以加強可讀性。Maven經過surfire插件自動生成Tests後綴的測試類。
多個測試類能夠組合成test suite。運行test suite將在該suite按照指定的順序執行全部測試類。下面演示包含兩個測試類 (MyClassTest和MySecondClassTest) 的測試集,經過@Suite.SuiteClasses statement能夠增長測試類。
import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) @SuiteClasses({ MyClassTest.class, MySecondClassTest.class }) public class AllTests { }
test suite中也能夠包含test suite。
經過標準的Java代碼能夠在Eclipse以外運行JUnit測試。Apache Maven的或Gradle框架之類的構建框架一般與持續集成服務器(如Hudson或Jenkins)組合按期自動執行測試。
org.junit.runner.JUnitCore類的runClasses()方法容許運行測試類,返回爲org.junit.runner.Result對象,包含測試結果信息。
下面類演示如何運行MyClassTest。這個類將執行測試類,輸出潛在錯誤到控制檯。
import org.junit.runner.JUnitCore; import org.junit.runner.Result; import org.junit.runner.notification.Failure; public class MyTestRunner { public static void main(String[] args) { Result result = JUnitCore.runClasses(MyClassTest.class); for (Failure failure : result.getFailures()) { System.out.println(failure.toString()); } } }
以上類能夠像Java類同樣從命令行運行,你只須要添加JUnit Jar到classpath便可。
Junit4.*使用註解(annotation)標識測試方法和配置測試,如下是經常使用相關annotation的簡要說明。特別重要的註解以下:
註解 | 描述 |
@Test public void method() |
標識方法爲測試方法 |
@Test (expected = Exception.class) | 標識拋出指定的異常 |
@Test(timeout=100) | 超時,單位ms |
@Before public void method() |
每一個測試執行以前的準備工做。用來準備測試環境,如讀取輸入數據,初始化類等。 |
@After public void method() |
每一個測試執行以後的清理工做。如刪除臨時文件,恢復初始設置,釋放內存等。 |
@BeforeClass public static void method() |
每一個測試集執行以前的準備工做。用來執行費時任務,如鏈接數據庫。必須是Static方法。 |
@AfterClass public static void method() |
每一個測試集執行以後的清理工做。用來清理,如斷開數據庫鏈接,必須是Static方法。 |
@Ignore or @Ignore("Why disabled") | 忽略指定測試方法,用於測試類還沒有準備好等狀況,使用時最好標明忽略的緣由。 |
@RunWith會替代默認的org.junit.runner.Runner類,好比:
@RunWith(Suite.class) public class MySuite { }
Mock也須要使用註解。
import org.junit.runner.RunWith; import org.junit.runners.Suite;
@RunWith(Suite.class) @Suite.SuiteClasses({ AssertTest.class, TestExecutionOrder.class, Assumption.class }) public class TestSuite { }
JUnit會假定全部測試方法按任意順序執行,也就是說,一個測試不該該依賴其它測試。
Junit 4.11容許你用Annoation以字母順序對測試方法進行排序,使用方法爲用@FixMethodOrder(MethodSorters.NAME_ASCENDING)。默認使用固定但不可預期的順序,對應參數爲`MethodSorters.DEFAULT`,也可使用`MethodSorters.JVM`,表明JVM的默認方式,每次運行順序會不一樣。
package org.hamcrest.examples.tutorial;
import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
public class IsNotANumber extends TypeSafeMatcher<Double> {
@Override
public boolean matchesSafely(Double number) {
return number.isNaN();
}
public void describeTo(Description description) {
description.appendText("not a number");
}
@Factory
public static <T> Matcher<Double> notANumber() {
return new IsNotANumber();
}
}
使用:
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.examples.tutorial.IsNotANumber.notANumber;
import junit.framework.TestCase;
public class NumberTest extends TestCase {
public void testSquareRootOfMinusOneIsNotANumber() {
assertThat(Math.sqrt(-1), is(notANumber()));
}
}
切記,要靜態導入notANumber, 參考資料:https://code.google.com/p/hamcrest/wiki/Tutorial。
稍微複雜點的實例:
package com.example; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Factory; import org.hamcrest.Matcher; public class LessThanOrEqual<T extends Comparable<T>> extends BaseMatcher<Comparable<T>> { private final Comparable<T> expValue; public LessThanOrEqual(T expValue) { this.expValue= expValue; } @Override public void describeTo(Description desc) { desc.appendText(" less than or equal(<=)" +expValue); } @Override public boolean matches(Object t) { int compareTo = expValue.compareTo((T)t); return compareTo > -1; } @Factory public static<T extends Comparable<T>> Matcher<T> lessThanOrEqual(T t) { return new LessThanOrEqual(t); } }
使用:
@Test public void lessthanOrEquals_matcher() throws Exception { int actualGoalScored = 2; int expGoalScored= 4; assertThat(actualGoalScored, lessThanOrEqual(expGoalScored)); expGoalScored =2; assertThat(actualGoalScored, lessThanOrEqual(expGoalScored )); double actualDouble = 3.14; double expDouble = 9.00; assertThat(actualDouble, lessThanOrEqual(expDouble)); String authorName = "Sujoy"; String expAuthName = "Zachary"; assertThat(authorName, lessThanOrEqual(expAuthName)); }
在Gradle編譯時使用Junit:
apply plugin: 'java' dependencies { testCompile 'junit:junit:4.12' }
Maven:
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
Eclipse 等IDE自帶Junit。
Eclipse對Junit的支持
Eclipse有建立JUnit測試的嚮導。例如,要爲現有類建立JUnit測試或測試類,在類單擊鼠標右鍵,New → JUnit Test Case。File → New → Other... → Java→ JUnit也可打開相似窗口。
執行:在類單擊鼠標右鍵Run-as →JUnit Test,執行類中全部測試。Alt+Shift+X, ,T,若是光標在測試裏面,只會執行當前測試。
只看失敗的測試:
測試失敗時才彈出:
拷貝錯誤信息
JUnit的靜態導入
靜態導入容許在類中定義的public static字段和方法不指定類就可使用。
// without static imports you have to write the following statement Assert.assertEquals("10 x 5 must be 50", 50, tester.multiply(10, 5)); // alternatively define assertEquals as static import import static org.junit.Assert.assertEquals; // more code // use assertEquals directly because of the static import assertEquals("10 x 5 must be 50", 50, tester.multiply(10, 5));
Eclipse中能夠配置自動靜態導入:Window → Preferences and select Java → Editor → Content Assist → Favorites.
org.junit.Assert
org.hamcrest.CoreMatchers
org.hamcrest.Matchers
這樣就可使用Content Assist(快捷方式Ctrl+Space)添加方法導入。
註解:@Test (expected = Exception.class)能夠測試單個異常。
測試多個異常的方法:
try { mustThrowException(); fail(); } catch (Exception e) { // expected // could also check for message of exception, etc. }
JUnit的插件測試爲插件書寫單元測試。這些測試運行特殊的測試執行器,在單獨的虛擬機中生成Eclipse實例。
使用註解@RunWith(Parameterized.class)便可。
測試類必須包含@Parameters註解的靜態方法返回數組的集合,用於做爲測試方法的參數。public域使用@parameter註解能夠取得測試注入測試值。
package first; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import java.util.Arrays; import java.util.Collection; import static org.junit.Assert.assertEquals; import static org.junit.runners.Parameterized.*; @RunWith(Parameterized.class) public class ParameterizedTestFields { // fields used together with @Parameter must be public @Parameter public int m1; @Parameter (value = 1) public int m2; // creates the test data @Parameters public static Collection<Object[]> data() { Object[][] data = new Object[][] { { 1 , 2 }, { 5, 3 }, { 121, 4 } }; return Arrays.asList(data); } @Test public void testMultiplyException() { MyClass tester = new MyClass(); assertEquals("Result", m1 * m2, tester.multiply(m1, m2)); } // class to be tested class MyClass { public int multiply(int i, int j) { return i *j; } } }
使用構造方法也能夠實現相似的效果:
package first; import static org.junit.Assert.assertEquals; import java.util.Arrays; import java.util.Collection; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class ParameterizedTestUsingConstructor { private int m1; private int m2; public ParameterizedTestUsingConstructor(int p1, int p2) { m1 = p1; m2 = p2; } // creates the test data @Parameters public static Collection<Object[]> data() { Object[][] data = new Object[][] { { 1 , 2 }, { 5, 3 }, { 121, 4 } }; return Arrays.asList(data); } @Test public void testMultiplyException() { MyClass tester = new MyClass(); assertEquals("Result", m1 * m2, tester.multiply(m1, m2)); } // class to be tested class MyClass { public int multiply(int i, int j) { return i *j; } } }
Rule能夠靈活的增長或者重定義測試類每一個測試方法的行爲。經過@Rule註解能夠建立和配置在測試方法使用的對象。好比靈活地指定異常:
package first; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; public class RuleExceptionTesterExample { @Rule public ExpectedException exception = ExpectedException.none(); @Test public void throwsIllegalArgumentExceptionIfIconIsNull() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Negative value not allowed"); ClassToBeTested t = new ClassToBeTested(); t.methodToBeTest(-1); } }
注意上述代碼只作演示,不能實際執行。
JUnit的已經提供了規則的幾個有用的實現。例如, TemporaryFolder類在測試執行完畢後會刪除文件。
package first; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; public class RuleTester { @Rule public TemporaryFolder folder = new TemporaryFolder(); @Test public void testUsingTempFolder() throws IOException { File createdFolder = folder.newFolder("newfolder"); File createdFile = folder.newFile("myfilefile.txt"); assertTrue(createdFile.exists()); } }
實現TestRule接口可自定義Rule。這個接口的apply(Statement, Description)方法返回Statement實例。Statement即JUnit運行時中的測試,Statement#evaluate()運行它們。Description 描述了單個測試。它經過反射閱讀測試信息。
下例子添加日誌語句到Android應用。
import android.util.Log; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; public class MyCustomRule implements TestRule { private Statement base; private Description description; @Override public Statement apply(Statement base, Description description) { this.base = base; this.description = description; return new MyStatement(base); } public class MyStatement extends Statement { private final Statement base; public MyStatement(Statement base) { this.base = base; } @Override public void evaluate() throws Throwable { Log.w("MyCustomRule",description.getMethodName() + "Started"); try { base.evaluate(); } finally { Log.w("MyCustomRule",description.getMethodName() + "Finished"); } } } }
使用Rule:
@Rule public MyCustomRule myRule = new MyCustomRule();
更多關於Rule的資料:https://github.com/junit-team/junit/wiki/Rules。
public interface FastTests { /* category marker */ } public interface SlowTests { /* category marker */ } public class A { @Test public void a() { fail(); } @Category(SlowTests.class) @Test public void b() { } } @Category({ SlowTests.class, FastTests.class }) public class B { @Test public void c() { } } @RunWith(Categories.class) @IncludeCategory(SlowTests.class) @SuiteClasses({ A.class, B.class }) // Note that Categories is a kind of Suite public class SlowTestSuite { // Will run A.b and B.c, but not A.a } @RunWith(Categories.class) @IncludeCategory(SlowTests.class) @ExcludeCategory(FastTests.class) @SuiteClasses({ A.class, B.class }) // Note that Categories is a kind of Suite public class SlowTestSuite { // Will run A.b, but not A.a or B.c }
本部分參考資料:https://github.com/junit-team/junit/blob/master/doc/ReleaseNotes4.8.md
python開發自動化測試羣291184506 PythonJava單元白盒測試羣144081101