软件测试是 执行的软件以验证代码状态(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