Junit4

Junit4

https://github.com/junit-team/junit4/wiki

断言

Junit提供了重载的原生类型、对象、数组(原生类型或对象)断言方法。参数的顺序是期待值和实际值。或者第一个参数可以是当测试失败的时候输出一个String类型的错误消息。有一个稍微不同的断言assertThat,assertThat参数为一个可选的错误消息,实际值和一个Matcher对象。注意 assertThat期待值与实际值的顺序与其他断言方法相反

每个断言方法表示如下:

import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.both;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.everyItem;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;

import java.util.Arrays;

import org.hamcrest.core.CombinableMatcher;
import org.junit.Test;

public class AssertTests {
  @Test
  public void testAssertArrayEquals() {
    byte[] expected = "trial".getBytes();
    byte[] actual = "trial".getBytes();
    assertArrayEquals("failure - byte arrays not same", expected, actual);
  }

  @Test
  public void testAssertEquals() {
    assertEquals("failure - strings are not equal", "text", "text");
  }

  @Test
  public void testAssertFalse() {
    assertFalse("failure - should be false", false);
  }

  @Test
  public void testAssertNotNull() {
    assertNotNull("should not be null", new Object());
  }

  @Test
  public void testAssertNotSame() {
    assertNotSame("should not be same Object", new Object(), new Object());
  }

  @Test
  public void testAssertNull() {
    assertNull("should be null", null);
  }

  @Test
  public void testAssertSame() {
    Integer aNumber = Integer.valueOf(768);
    assertSame("should be same", aNumber, aNumber);
  }

  // JUnit Matchers assertThat
  @Test
  public void testAssertThatBothContainsString() {
    assertThat("albumen", both(containsString("a")).and(containsString("b")));
  }

  @Test
  public void testAssertThatHasItems() {
    assertThat(Arrays.asList("one", "two", "three"), hasItems("one", "three"));
  }

  @Test
  public void testAssertThatEveryItemContainsString() {
    assertThat(Arrays.asList(new String[] { "fun", "ban", "net" }), everyItem(containsString("n")));
  }

  // Core Hamcrest Matchers with assertThat
  @Test
  public void testAssertThatHamcrestCoreMatchers() {
    assertThat("good", allOf(equalTo("good"), startsWith("good")));
    assertThat("good", not(allOf(equalTo("bad"), equalTo("good"))));
    assertThat("good", anyOf(equalTo("bad"), equalTo("good")));
    assertThat(7, not(CombinableMatcher.<Integer> either(equalTo(3)).or(equalTo(4))));
    assertThat(new Object(), not(sameInstance(new Object())));
  }

  @Test
  public void testAssertTrue() {
    assertTrue("failure - should be true", true);
  }
}

测试 fixtures

有四个fixture注解:两个用于类级别的fixture,另外两个用于方法级别的fixture。 在类级别,有@BeforeClass和@AfterClass,在方法(或测试)级别,有@Before和@After。

package test;

import java.io.Closeable;
import java.io.IOException;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

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()");
  }
}

输出如下:

@BeforeClass setUpClass
@Before setUp
@Test test2()
@After tearDown
@Before setUp
@Test test1()
@After tearDown
@AfterClass tearDownClass

测试执行顺序

按照设计,Junit没有指定测试方法调用的执行顺序。到目前为止,方法只是按照反射API返回的顺序进行调用。但是,使用JVM命令是不明智的,因为Java平台没有指定任何特定的顺序,实际上JDK 7返回或多或少的随机顺序。当然,精心编写的测试代码不会做任何顺序的假设,但有些则需要,在某个特定的平台上一个可预测的测试失败要好与随机的测试失败。

从4.11版开始,JUnit将默认使用确定性但不可预测的顺序(MethodSorters.DEFAULT)。要更改测试执行顺序,只需使用@FixMethodOrder对测试类进行注明,并指定一个可用的MethodSorters:

  • @FixMethodOrder(MethodSorters.JVM):按照JVM返回的顺序执行测试方法。这个顺序每次运行可能都不一样。

  • @FixMethodOrder(MethodSorters.NAME_ASCENDING):按照字典顺序对测试方法名排序。

import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
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");
    }
}

以上代码会按照测试方法名的升序顺序执行

Suite聚合测试

使用Suite做为Runner允许我们手动创建一个包含其他类的suite。同JUnit 3.8.x 的static Test suite()方法一样。使用Suite,需要在类上使用注解@RunWith(Suite.class) 和 @SuiteClasses(TestClass1.class, …)。当你运行这个类的时候,它会运行suite中所有类的测试。

下面的类注明了suite注解,并且不需要其他实现。注意@RunWith注解,它指定使用JUnit 4测试Runner,org.junit.runners.Suite来运行这个特定的测试类。他需要与@Suite注解一起使用,@Suite告诉Suite Runner包含的测试类和顺序。

import org.junit.runner.RunWith;
import org.junit.runners.Suite;

@RunWith(Suite.class)
@Suite.SuiteClasses({
  TestFeatureLogin.class,
  TestFeatureLogout.class,
  TestFeatureNavigate.class,
  TestFeatureUpdate.class
})

public class FeatureTestSuite {
  // the class remains empty,
  // used only as a holder for the above annotations
}

忽略测试

如果由于某些原因,你不希望测试失败,你只想忽略它,你可以暂时禁用一个测试。

要忽略JUnit中的测试,你可以将方法注释掉,或者删除@Test注解; 但测试Runner不会报告这样的测试。 或者,你可以在@Test之前或之后添加@Ignore注解。 测试Runner将报告忽略测试的数量,以及运行的测试数量和失败的测试数量。

注意,如果要记录测试被忽略的原因,可以使用@Ignore的可选参数(一个字符串)

@Ignore("Test is ignored as a demonstration")
@Test
public void testSame() {
    assertThat(1, is(1));
}

期望异常

你是如何验证代码是否按期望抛出异常? 验证代码正常完成很重要,但确保代码在特殊情况下按预期运行也至关重要。 例如:

new ArrayList<Object>().get(0);

该代码应该抛出一个IndexOutOfBoundsException。 @Test注解有一个可选参数“expected”,它作为Throwable的子类。 如果我们想验证ArrayList抛出正确的异常,我们会写:

@Test(expected = IndexOutOfBoundsException.class) 
public void empty() { 
     new ArrayList<Object>().get(0); 
}

应小心使用expected参数。 如果方法中的任何代码抛出IndexOutOfBoundsException异常,上述测试将通过。 对于较长的测试,建议使用如下描述的ExpectedException规则。

上述方法对简单的情况是有用的,但它也有其局限性。 例如,不能测试异常的消息值,或者异常抛出后域对象的状态。

Try/Catch

为了解决这个问题,你可以使用JUnit 3.x中的try / catch:

@Test
public void testExceptionMessage() {
    try {
        new ArrayList<Object>().get(0);
        fail("Expected an IndexOutOfBoundsException to be thrown");
    } catch (IndexOutOfBoundsException anIndexOutOfBoundsException) {
        assertThat(anIndexOutOfBoundsException.getMessage(), is("Index: 0, Size: 0"));
    }
}
期望异常规则

或者,使用ExpectedException规则。 此规则可以让你不仅指出你期望的是什么异常,还可以指出你期望的异常消息

@Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void shouldTestExceptionMessage() throws IndexOutOfBoundsException {
    List<Object> list = new ArrayList<Object>();

    thrown.expect(IndexOutOfBoundsException.class);
    thrown.expectMessage("Index: 0, Size: 0");
    list.get(0); // execution will never get past this line
}

期望消息还可以使用Matchers,这可以让你在测试中有更大的灵活性,例如:

thrown.expectMessage(Matchers.containsString("Size: 0"));

此外,可以你可以使用Matchers来检查异常,如果它具有要验证的嵌入状态,那么它将非常有用。例如:

import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.startsWith;

import javax.ws.rs.NotFoundException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

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);
        }
    }
}

有关ExpectedException规则的延伸讨论,请参阅此博文

测试超时

如果一个测试“失控”或花费太长时间,可能会自动失败。有两种可选方法可以实现此行为

在@Test注解上使用超时参数(应用于测试方法)

您可以选择以毫秒为单位指定超时,以使测试方法在超过该毫秒数的时间内失败。 如果超出时间限制,则会因为抛出异常而触发测试失败:

@Test(timeout=1000)
public void testWithTimeout() {
  ...
}
超时规则(应用于测试类的所有测试用例)

超时规则对类中的所有测试方法应用相同的超时

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;

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 
    }
}

Matchers and assertthat

Joe Walnes在JMock 1之上建立了一个新的断言机制。方法名称是assertThat,语法如下所示:

assertThat(x, is(3));
assertThat(x, is(not(4)));
assertThat(responseString, either(containsString("color")).or(containsString("colour")));
assertThat(myList, hasItem("3"));

通用语法:

assertThat([value], [matcher statement]);

此断言语法的优点包括:

  • 更可读和可分类:

    这种语法允许你根据主语,动词,对象(assert“x is 3”)来考虑

    而不是assertEquals,它使用动词,对象,主语(assert“等于3 x”)

  • 组合:Matchers语句可以是

    否定(not(s))

    组合((either(s).or(t))

    映射到集合(each(s))

    或者使用自定义组合(afterFiveSeconds(s))

比较:

assertTrue(responseString.contains("color") || responseString.contains("colour"));
// ==> failure message: 
// java.lang.AssertionError:


assertThat(responseString, anyOf(containsString("color"), containsString("colour")));
// ==> failure message:
// java.lang.AssertionError: 
// Expected: (a string containing "color" or a string containing "colour")
//      got: "Please choose a font"

更多Matchers参考:Matchers

Parameterized

Parameterized测试可以使用不同参数多次运行同一个测试。

比如说测试斐波那契数列:

数列
F ( 0 )0
F ( 1 )1
F ( 2 )1
F ( 3 )2
F ( 4 )3
F ( 5 )5
F ( 6 )8
F ( n )F ( n - 1 ) + F ( n - 2 )( n >= 2,n ∈ N*)
构造方法方式
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 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) {
        fInput= input;
        fExpected= expected;
    }

    @Test
    public void test() {
        //fInput = 0,fExpected = 0 测试1次
        //fInput = 1,fExpected = 1 测试1次
        //fInput = 2,fExpected = 1 测试1次
        //依次类推,一共测试 6 次
        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;
    }
}
属性注入方式

当前只能用于public属性

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.Parameter;
import org.junit.runners.Parameterized.Parameters;

@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 {
    ...
}

Rule

Rule允许在测试类中非常灵活地添加或重新定义每个测试方法的行为。 测试人员可以重复使用或扩展以Rule,也可以自行编写。

测试方法名规则

TestName Rule 可以在测试方法中使用当前测试方法名:

public class NameRuleTest {
  @Rule
  public final TestName name = new TestName();

  @Test
  public void testA() {
    // name.getMethodName() 会获取当前测试方法名
    assertEquals("testA", name.getMethodName());
  }

  @Test
  public void testB() {
    assertEquals("testB", name.getMethodName());
  }
}
超时规则

Timeout Rule 对类中的所有测试方法应用相同的超时时间

public static class HasGlobalTimeout {
  public static String log;

  @Rule
  public final TestRule globalTimeout = Timeout.millis(20);

  @Test
  public void testInfiniteLoop1() {
    // 死循环,肯定会超时,报TestTimedOutException异常
    log += "ran1";
    for(;;) {}
  }

  @Test
  public void testInfiniteLoop2() {
    log += "ran2";
    for(;;) {}
  }
}
异常规则

ExpectedException Rules 允许对预期的异常类型和消息进行测试

public static class HasExpectedException {
  @Rule
  public final ExpectedException thrown = ExpectedException.none();

  @Test
  public void throwsNothing() {

  }

  @Test
  public void throwsNullPointerException() {
    thrown.expect(NullPointerException.class);
    throw new NullPointerException();
  }

  @Test
  public void throwsNullPointerExceptionWithMessage() {
    thrown.expect(NullPointerException.class);
    thrown.expectMessage("happened?");
    thrown.expectMessage(startsWith("What"));
    throw new NullPointerException("What happened?");
  }
}

更多Rule或者自定义Rule参考 https://github.com/junit-team/junit4/wiki/Rules

junit4和powermock是两个常用的Java测试框架,它们常被开发人员用来进行单元测试。 junit4是一个成熟的单元测试框架,用于编写和运行Java程序的测试用例。它可以帮助开发人员在开发过程中及时发现代码中的错误,提升代码的质量。junit4提供了一系列的注解和断言来简化测试用例的编写,并且可以与各种开发工具和持续集成工具进行集成。 与此同时,powermock是一个用于扩展junit4的工具,可以帮助开发人员进行更加灵活和强大的单元测试。它可以模拟和修改Java程序中的一些难以测试的对象和行为,例如静态方法、私有方法和构造函数等。powermock使用了一种称为“字节码操作”的技术,能够改变Java字节码的执行路径,从而达到模拟和修改的目的。 使用junit4和powermock可以让开发人员编写更加全面和准确的单元测试用例。在测试过程中,我们可以使用junit4来编写测试用例的框架和断言,而使用powermock来模拟和修改需要被测试的对象。这样就可以在保持单元测试的独立性和可重复性的同时,更好地模拟和验证代码中的行为。 总的来说,junit4和powermock是两个非常有用的Java测试框架。它们可以一起使用,为开发人员提供更强大和灵活的单元测试能力。通过编写全面和准确的单元测试用例,可以提高代码的质量和可维护性,同时也可以减少程序中的BUG和错误。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值