1.断言assert
JUnit为所有基本类型和对象和数组(基元或对象)提供重载的断言方法。参数顺序是预期值,后跟实际值。第一个参数可以是失败时输出的String消息(可选)。断言略有不同,assertThat
它具有可选失败消息,实际值和Matcher
对象的参数。请注意,与其他断言方法相比,预期和实际相反。具体api查看github :https://github.com/junit-team/junit4/wiki/Assertions
@Test
public void testAssertArrayEquals() {
byte[] expected = "trial".getBytes();
byte[] actual = "trial".getBytes();
assertArrayEquals("failure - byte arrays not same", expected, actual);
}
2.组合测试
使用Suite
可以手动构建包含许多类测试的套件。要使用它,请使用@RunWith(Suite.class)
和注释一个类@SuiteClasses(TestClass1.class, ...)
。借助一个空类来运行所有包含的测试类的所有方法
@RunWith(Suite.class)
@Suite.SuiteClasses({
UserControllerTest.class,
User.class,
})
public class FeatureTestSuite {
}
3.Junit执行顺序
根据设计,JUnit不指定测试方法调用的执行顺序。到目前为止,这些方法只是按反射API返回的顺序调用。但是,使用JVM顺序是不明智的,因为Java平台没有指定任何特定顺序,实际上JDK 7返回或多或少的随机顺序。从版本4.11开始,JUnit将默认使用确定性但不可预测的顺序(MethodSorters.DEFAULT)
。要更改测试执行顺序,只需使用@FixMethodOrder
并指定一个可用的MethodSorters 来注释您的测试类:
@FixMethodOrder(MethodSorters.JVM)
:按JVM返回的顺序保留测试方法。此订单可能因运行而异。@FixMethodOrder(MethodSorters.NAME_ASCENDING)
:按字典顺序按方法名称对测试方法进行排序。
@FixMethodOrder(MethodSorters.DEFAULT)
public class TestMethodOrder {
@Test
public void testA() {
System.out.println("first");
}
@Test
public void testB() {
System.out.println("second");
}
@Test
public void testC() {
System.out.println("third");
}
}
4.异常测试
如何验证代码是否按预期抛出异常?验证代码是否正常完成非常重要,但确保代码在特殊情况下的行为符合预期也至关重要。
下方demo表示如果抛出异常则测试正常通过
@Test(expected = IndexOutOfBoundsException.class)
public void empty() {
new ArrayList<Object>().get(0);
}
使用@Rule定义异常规则,当符合预期规则的时候则正常运行
public class TestExy {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void shouldThrow() {
TestThing testThing = new TestThing();
thrown.expect(NotFoundException.class);
thrown.expectMessage(startsWith("some Message"));
thrown.expect(hasProperty("response", hasProperty("status", is(404))));
testThing.chuck();
}
private class TestThing {
public void chuck() {
Response response = Response.status(Status.NOT_FOUND).entity("Resource not found").build();
throw new NotFoundException("some Message", response);
}
}
}
5.断言匹配
具可读性和可输入性:这种语法允许您根据主语,动词,对象(断言“x是3”)来思考,而不是assertEquals
使用动词,对象,主语(断言“等于3 x”)
组合:任何匹配器语句都可以是negated(not(s)
),combined(either(s).or(t)
),映射到集合(each(s)
),或用于自定义组合(afterFiveSeconds(s)
)
assertThat(x, is(3));
assertThat(x, is(not(4)));
assertThat(responseString, either(containsString("color")).or(containsString("colour")));
assertThat(myList, hasItem("3"));
assertThat(responseString, anyOf(containsString("color"), containsString("colour")));
6.忽略测试
忽略JUnit中的测试,您可以注释方法或删除@Test
注释; 但是测试运行员不会报告这样的测试。或者,您可以@Ignore
在前面或后面添加注释@Test
。测试运行器将报告被忽略的测试数量,以及运行的测试数量和失败的测试数量。
@Test
@Ignore("Test is ignored as a demonstration")
public void testA() {
System.out.println("first");
}
7.测试超时
测试“失控”或耗时太长,可能会自动失败。有两种方法可以实现此行为:
@Test(timeout = 1000)
public void testWithTimeout() throws InterruptedException {
Thread.sleep(2000);
}
使用@Rule时候,将会对本类的所有方法进行超时规则测试
public class HasGlobalTimeout {
public static String log;
private final CountDownLatch latch = new CountDownLatch(1);
@Rule
public Timeout globalTimeout = Timeout.seconds(10); // 10 seconds max per method tested
@Test
public void testSleepForTooLong() throws Exception {
log += "ran1";
TimeUnit.SECONDS.sleep(100); // sleep for 100 seconds
}
@Test
public void testBlockForever() throws Exception {
log += "ran2";
latch.await(); // will block
}
}
8.参数化测试
自定义运行器Parameterized
实现参数化测试。运行参数化测试类时,将会通过参数构造不同的实例并且进行单元测试
当拥有构造器的时候,成员变量可以不加#@Parmeter注解
@RunWith(Parameterized.class)
public class FibonacciTest {
@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{ 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 }
});
}
private int fInput;
private int fExpected;
public FibonacciTest(int input, int expected) {
this.fInput = input;
this.fExpected = expected;
}
@Test
public void test() {
assertEquals(fExpected, Fibonacci.compute(fInput));
}
}
当没有构造器的时候,将会使用@Parameter 分别设置成员变量,记得默认为0,所以如果有多个参数要按照顺序写上序号
@RunWith(Parameterized.class)
public class FibonacciTest {
@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{ 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 }
});
}
@Parameter // first data value (0) is default
public /* NOT private */ int fInput;
@Parameter(1)
public /* NOT private */ int fExpected;
@Test
public void test() {
assertEquals(fExpected, Fibonacci.compute(fInput));
}
}
public class Fibonacci {
public static int compute(int n) {
int result = 0;
if (n <= 1) {
result = n;
} else {
result = compute(n - 1) + compute(n - 2);
}
return result;
}
}
单个参数的测试
@Parameters
public static Iterable<? extends Object> data() {
return Arrays.asList("first test", "second test");
}
@Parameters
public static Object[] data() {
return new Object[] { "first test", "second test" };
}
9.假设
理想情况下,编写测试的开发人员可以控制可能导致测试失败的所有力。如果不能立即实现,那么明确依赖关系通常可以改进设计。例如,如果测试在与开发人员预期不同的语言环境中运行时失败,则可以通过将语言环境显式传递给域代码来修复它。
@Test
public void filenameIncludesUsername() {
assumeThat(1, is(0));
System.out.println("I'm here"); // 这句不会被执
}
10.RULE规则
@Rule 代表的是一个重写了@Before和@After的类,由于一些公用的@Before方法可以提取出来,所以有了rule
TemporaryFolder
TemporaryFolder类是junit提供的关于文件的默认类,提供了文件创建和文件删除的功能
public static class HasTempFolder {
@Rule
public final TemporaryFolder folder = new TemporaryFolder();
@Test
public void testUsingTempFolder() throws IOException {
File createdFile = folder.newFile("myfile.txt");
File createdFolder = folder.newFolder("subfolder");
// ...
}
}
从源码可以看出,在进行单元测试的时候,会执行before和after的方法
protected void after() {
this.delete();
}
public void create() throws IOException {
this.folder = this.createTemporaryFolderIn(this.parentFolder);
}
public void delete() {
if (this.folder != null) {
this.recursiveDelete(this.folder);
}
}
public void create() throws IOException {
this.folder = this.createTemporaryFolderIn(this.parentFolder);
}
ErrorCollector
用于收集异常信息,不会因为一个异常而终止程序运行
public class UsesErrorCollectorTwice {
@Rule
public final ErrorCollector collector = new ErrorCollector();
@Test
public void example() {
collector.addError(new Throwable("first thing went wrong"));
collector.addError(new Throwable("second thing went wrong"));
}
}
TestWatcher
有点类似监听单元测试的运行状态,可以再程序的成功或者失败的时候调用某些方法
public class WatchmanTest {
private static String watchedLog;
@Rule
public final TestRule watchman = new TestWatcher() {
@Override
public Statement apply(Statement base, Description description) {
return super.apply(base, description);
}
@Override
protected void succeeded(Description description) {
watchedLog += description.getDisplayName() + " " + "success!\n";
}
@Override
protected void failed(Throwable e, Description description) {
watchedLog += description.getDisplayName() + " " + e.getClass().getSimpleName() + "\n";
}
@Override
protected void skipped(AssumptionViolatedException e, Description description) {
watchedLog += description.getDisplayName() + " " + e.getClass().getSimpleName() + "\n";
}
@Override
protected void starting(Description description) {
super.starting(description);
}
@Override
protected void finished(Description description) {
super.finished(description);
}
};
@Test
public void fails() {
fail();
}
@Test
public void succeeds() {
}
}
TestName
用于获取当前测试单元的方法名
public class NameRuleTest {
@Rule
public final TestName name = new TestName();
@Test
public void testA() {
assertEquals("testA", name.getMethodName());
}
@Test
public void testB() {
assertEquals("testB", name.getMethodName());
}
}
Timeout
给所有方法第一个超时时间
public static class HasGlobalTimeout {
public static String log;
@Rule
public final TestRule globalTimeout = Timeout.millis(20);
@Test
public void testInfiniteLoop1() {
log += "ran1";
for(;;) {}
}
@Test
public void testInfiniteLoop2() {
log += "ran2";
for(;;) {}
}
}
其他请参考github
https://github.com/junit-team/junit4/wiki/Rules
11.Theroy
没看懂
12.Test fixtures
大概是在单元测试的前后,类启动的前后进行一些方法的调用
public class TestFixturesExample {
static class ExpensiveManagedResource implements Closeable {
@Override
public void close() throws IOException {}
}
static class ManagedResource implements Closeable {
@Override
public void close() throws IOException {}
}
@BeforeClass
public static void setUpClass() {
System.out.println("@BeforeClass setUpClass");
myExpensiveManagedResource = new ExpensiveManagedResource();
}
@AfterClass
public static void tearDownClass() throws IOException {
System.out.println("@AfterClass tearDownClass");
myExpensiveManagedResource.close();
myExpensiveManagedResource = null;
}
private ManagedResource myManagedResource;
private static ExpensiveManagedResource myExpensiveManagedResource;
private void println(String string) {
System.out.println(string);
}
@Before
public void setUp() {
this.println("@Before setUp");
this.myManagedResource = new ManagedResource();
}
@After
public void tearDown() throws IOException {
this.println("@After tearDown");
this.myManagedResource.close();
this.myManagedResource = null;
}
@Test
public void test1() {
this.println("@Test test1()");
}
@Test
public void test2() {
this.println("@Test test2()");
}
}
13.分类组合测试
从下面的注释和注解可以非常容易看出是通过接口来进行方法的分类
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
}