Junit使用

1、創建Junit測試類java

1. 右擊test測試包,選擇New-->Other...web

 

2. 在窗口中找到Junit,選擇Junit Test Casespring

 

3. 輸入名稱(Name),命名規則通常建議採用:類名+Test。Browse...選擇要測試的類,這裏是StudentService。數據庫

 

4. 勾選要測試的方法數組

 

5. 生成後,效果以下:session

 

這裏import static是引入Assert類中靜態屬性或靜態方法的寫法。原來要Assert.fail(),如今只需直接fial()便可,即省略了Assert。app

 

其實不經過Junit新建嚮導來創建也能夠,隨便創建一個新類後,只需在方法上加入@Test註解便可。oop

 

 

2、核心——斷言性能

斷言是編寫測試用例的核心實現方式,即指望值是多少,測試的結果是多少,以此來判斷測試是否經過。單元測試

1. 斷言核心方法

assertArrayEquals(expecteds, actuals)

查看兩個數組是否相等。

assertEquals(expected, actual)

查看兩個對象是否相等。相似於字符串比較使用的equals()方法

assertNotEquals(first, second)

查看兩個對象是否不相等。

assertNull(object)

查看對象是否爲空。

assertNotNull(object)

查看對象是否不爲空。

assertSame(expected, actual)

查看兩個對象的引用是否相等。相似於使用「==」比較兩個對象

assertNotSame(unexpected, actual)

查看兩個對象的引用是否不相等。相似於使用「!=」比較兩個對象

assertTrue(condition)

查看運行結果是否爲true。

assertFalse(condition)

查看運行結果是否爲false。

assertThat(actual, matcher)

查看實際值是否知足指定的條件

fail()

讓測試失敗

2. 示例

package test;

 

import static org.hamcrest.CoreMatchers.*;

import static org.junit.Assert.*;

 

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

              org.junit.Assert.assertArrayEquals("failure - byte arrays not same", expected, actual);

            }

 

            @Test

            public void testAssertEquals() {

              org.junit.Assert.assertEquals("failure - strings not same", 5l, 5l);

            }

 

            @Test

            public void testAssertFalse() {

              org.junit.Assert.assertFalse("failure - should be false", false);

            }

 

            @Test

            public void testAssertNotNull() {

              org.junit.Assert.assertNotNull("should not be null", new Object());

            }

 

            @Test

            public void testAssertNotSame() {

              org.junit.Assert.assertNotSame("should not be same Object", new Object(), new Object());

            }

 

            @Test

            public void testAssertNull() {

              org.junit.Assert.assertNull("should be null", null);

            }

 

            @Test

            public void testAssertSame() {

              Integer aNumber = Integer.valueOf(768);

              org.junit.Assert.assertSame("should be same", aNumber, aNumber);

            }

 

            // JUnit Matchers assertThat

            @Test

            public void testAssertThatBothContainsString() {

              org.junit.Assert.assertThat("albumen", both(containsString("a")).and(containsString("b")));

            }

 

            @Test

            public void testAssertThathasItemsContainsString() {

              org.junit.Assert.assertThat(Arrays.asList("one", "two", "three"), hasItems("one", "three"));

            }

 

            @Test

            public void testAssertThatEveryItemContainsString() {

              org.junit.Assert.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())));

            }

}

 

 

3、核心——註解

1. 說明

@Before

初始化方法

@After

釋放資源

@Test

測試方法,在這裏能夠測試指望異常和超時時間

@Ignore

忽略的測試方法

@BeforeClass

針對全部測試,只執行一次,且必須爲static void

@AfterClass

針對全部測試,只執行一次,且必須爲static void

@RunWith

指定測試類使用某個運行器

@Parameters

指定測試類的測試數據集合

@Rule

容許靈活添加或從新定義測試類中的每一個測試方法的行爲

@FixMethodOrder

指定測試方法的執行順序

2. 執行順序

一個測試類單元測試的執行順序爲:

@BeforeClass –> @Before –> @Test –> @After –> @AfterClass

每個測試方法的調用順序爲:

@Before –> @Test –> @After

3. 示例

package test;

 

import static org.junit.Assert.*;

 

import org.junit.*;

 

public class JDemoTest {

 

          @BeforeClass

          public static void setUpBeforeClass() throws Exception {

                    System.out.println("in BeforeClass================");

          }        

 

          @AfterClass

          public static void tearDownAfterClass() throws Exception {

                    System.out.println("in AfterClass=================");

          }

 

          @Before

          public void before() {

                    System.out.println("in Before");

          }

 

          @After

          public void after() {

                    System.out.println("in After");

          }

 

          @Test(timeout = 10000)

          public void testadd() {

                    JDemo a = new JDemo();

                    assertEquals(6, a.add(3, 3));

                    System.out.println("in Test ----Add");

          }

 

          @Test

          public void testdivision() {

                    JDemo a = new JDemo();

                    assertEquals(3, a.division(6, 2));

                    System.out.println("in Test ----Division");

          }

 

          @Ignore

          @Test

          public void test_ignore() {

                    JDemo a = new JDemo();

                    assertEquals(6, a.add(1, 5));

                    System.out.println("in test_ignore");

          }

 

          @Test

          public void teest_fail() {

                    fail();

          }

}

 

class JDemo extends Thread {

 

          int result;

 

          public int add(int a, int b) {

                    try {

                               sleep(1000);

                               result = a + b;

                    } catch (InterruptedException e) {

                    }

                    return result;

          }

 

          public int division(int a, int b) {

                    return result = a / b;

          }

}

執行結果:

in BeforeClass================

in Before

in Test ----Add

in After

in Before

in Test ----Division

in After

in AfterClass=================

 

圖中左上紅框中部分表示Junit運行結果,5個成功(1個忽略),1個錯誤,1個失敗。(注意錯誤和失敗不是一回事,錯誤說明代碼有錯誤,而失敗表示該測試方法測試失敗)

左下紅框中則表示出了各個測試方法的運行狀態,能夠看到成功、錯誤、失敗、失敗各自的圖標是不同的,還能夠看到運行時間。

右邊部分則是異常堆棧,可查看異常信息。

4、實例總結

1. 參數化測試

有時一個測試方法,不一樣的參數值會產生不一樣的結果,那麼咱們爲了測試全面,會把多個參數值都寫出 來並一一斷言測試,這樣有時不免費時費力,這是咱們即可以採用參數化測試來解決這個問題。參數化測試就比如把一個「輸入值,指望值」的集合傳入給測試方 法,達到一次性測試的目的。

package test;

 

import static org.junit.Assert.*;

 

import java.util.Arrays;

 

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(name = "{index}: fib({0})={1}")

    public static Iterable<Object[]> data() {

        return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 },

                { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });

    }

 

    private int input;

    private int expected;

 

    public FibonacciTest(int input, int expected) {

        this.input = input;

        this.expected = expected;

    }

 

    @Test

    public void test() {

        assertEquals(expected, Fibonacci.compute(input));

    }

}

 

class Fibonacci {

 

          public static int compute(int input) {

                    int result;

                    switch (input) {

                    case 0:

                               result = 0;

                               break;

                    case 1:

                    case 2:

                               result = 1;

                               break;

                    case 3:

                               result = 2;

                               break;

                    case 4:

                               result = 3;

                               break;

                    case 5:

                               result = 5;

                               break;

                    case 6:

                               result = 8;

                               break;

                    default:

                               result = 0;

                    }

                    return result;

          }

}

@Parameters註解參數name,實際是測試方法名稱。因爲一個test()方法就完成了全部測試,那假如某一組測試數據有問題,那在Junit的結果頁面裏該如何呈現?所以採用name實際上就是區分每一個測試數據的測試方法名。以下圖:

 

2. 打包測試

一樣,若是一個項目中有不少個測試用例,若是一個個測試也很麻煩,所以打包測試就是一次性測試完成包中含有的全部測試用例。

package test;

 

import org.junit.runner.RunWith;

import org.junit.runners.Suite;

 

@RunWith(Suite.class)

@Suite.SuiteClasses({ AssertTests.class, FibonacciTest.class, JDemoTest.class })

public class AllCaseTest {

 

}

這個功能也須要使用一個特殊的Runner ,須要向@RunWith註解傳遞一個參數Suite.class 。同時,咱們還須要另一個註解@Suite.SuiteClasses,來代表這個類是一個打包測試類。並將須要打包的類做爲參數傳遞給該註解就能夠 了。至於AllCaseTest隨便起一個類名,內容爲空既可。運行AllCaseTest類便可完成打包測試

3. 異常測試

異常測試與普通斷言測試不一樣,共有三種方法,其中最爲靈活的是第三種,能夠與斷言結合使用

第一種:

  @Test(expected= IndexOutOfBoundsException.class)

  public void empty() {

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

  }

第二種:

  @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"));

      }

  }

第三種:

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

        Assert.assertEquals(1, list.get(0));

    }

在上述幾種方法中,不管是expected仍是expect都表示指望拋出的異常,假如某一方 法,當參數爲某一值時會拋出異常,那麼使用第一種方法就必須爲該參數單獨寫一個測試方法來測試異常,而沒法與其餘參數值一同寫在一個測試方法裏,因此顯得 累贅。第二種方法雖然解決這個問題,可是寫法不只繁瑣也不利於理解。而第三種犯法,不只能動態更改指望拋出的異常,與斷言語句結合的也很是好,所以推薦使 用該方法來測試異常。

4. 限時測試

有時爲了防止出現死循環或者方法執行過長(或檢查方法效率),而須要使用到限時測試。顧名思義,就是超出設定時間即視爲測試失敗。共有兩種寫法。

第一種:

@Test(timeout=1000)

public void testWithTimeout() {

  ...

}

第二種:

public class HasGlobalTimeout {

    public static String log;

 

    @Rule

    public Timeout globalTimeout = new Timeout(10000); // 10 seconds max per method tested

 

    @Test

    public void testInfiniteLoop1() {

        log += "ran1";

        for (;;) {

        }

    }

 

    @Test

    public void testInfiniteLoop2() {

        log += "ran2";

        for (;;) {

        }

    }

}

其中,第二種方法與異常測試的第三種方法的寫法相似。也是推薦的寫法。

 

 

 

1、會用Spring測試套件的好處

在開發基於Spring的應用時,若是你還直接使用Junit進行單元測試,那你就錯過了Spring爲咱們所提供的饕餮大餐了。使用Junit直接進行單元測試有如下四大不足:

1)致使屢次Spring容器初始化問題

根據JUnit測試方法的調用流程,每執行一個測試方法都會建立一個測試用例的實例並調用 setUp()方法。因爲通常狀況下,咱們在setUp()方法中初始化Spring容器,這意味着若是測試用例有多少個測試方法,Spring容器就會 被重複初始化屢次。雖然初始化Spring容器的速度並不會太慢,但因爲可能會在Spring容器初始化時執行加載Hibernate映射文件等耗時的操 做,若是每執行一個測試方法都必須重複初始化Spring容器,則對測試性能的影響是不容忽視的;

使用Spring測試套件,Spring容器只會初始化一次

2)須要使用硬編碼方式手工獲取Bean

在測試用例類中咱們須要經過ctx.getBean()方法從Spirng容器中獲取須要測試的目標Bean,而且還要進行強制類型轉換的造型操做。這種乏味的操做迷漫在測試用例的代碼中,讓人以爲煩瑣不堪;

使用Spring測試套件,測試用例類中的屬性會被自動填充Spring容器的對應Bean,無須在手工設置Bean!

3)數據庫現場容易遭受破壞

測試方法對數據庫的更改操做會持久化到數據庫中。雖然是針對開發數據庫進行操做,但若是數據操做 的影響是持久的,可能會影響到後面的測試行爲。舉個例子,用戶在測試方法中插入一條ID爲1的User記錄,第一次運行不會有問題,第二次運行時,就會因 爲主鍵衝突而致使測試用例失敗。因此應該既可以完成功能邏輯檢查,又可以在測試完成後恢復現場,不會留下「後遺症」;

使用Spring測試套件,Spring會在你驗證後,自動回滾對數據庫的操做,保證數據庫的現場不被破壞,所以重複測試不會發生問題!

4)不方便對數據操做正確性進行檢查

假如咱們向登陸日誌表插入了一條成功登陸日誌,但是咱們卻沒有對t_login_log表中是否 確實添加了一條記錄進行檢查。通常狀況下,咱們多是打開數據庫,肉眼觀察是否插入了相應的記錄,但這嚴重違背了自動測試的原則。試想在測試包括成千上萬 個數據操做行爲的程序時,如何用肉眼進行檢查?

只要你繼承Spring的測試套件的用例類,你就能夠經過jdbcTemplate(或Dao等)在同一事務中訪問數據庫,查詢數據的變化,驗證操做的正確性!

Spring提供了一套擴展於Junit測試用例的測試套件,使用這套測試套件徹底解決了以上四個問題,讓咱們測試Spring的應用更加方便。這個測試套件主要由org.springframework.test包下的若干類組成,使用簡單快捷,方便上手。

2、使用方法

1)基本用法

package com.test;
 
import javax.annotation.Resource;
 
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:config/applicationContext-*.xml", "classpath:services/ext/service-*.xml" })
public class UserServiceTest {
 
          @Resource
          private IUserService userService;
 
          @Test
          public void testAddOpinion1() {
                    userService.downloadCount(1);
                    System.out.println(1);
          }
 
          @Test
          public void testAddOpinion2() {
                    userService.downloadCount(2);
                    System.out.println(2);
          }
}

@RunWith(SpringJUnit4ClassRunner.class) 用於配置spring中測試的環境

@ContextConfiguration(locations = { "classpath:config/applicationContext-*.xml", "classpath:services/ext/service-*.xml" })用於指定配置文件所在的位置

@Resource注入Spring容器Bean對象,注意與@Autowired區別

2)事務用法

package com.test;
 
import javax.annotation.Resource;
 
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;
 
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:config/applicationContext-*.xml", "classpath:services/ext/service-*.xml" })
@Transactional
@TransactionConfiguration(transactionManager = "transactionManager")
//@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
public class UserServiceTest {
 
          @Resource
          private IUserService userService;
 
          @Test
//        @Transactional
          public void testAddOpinion1() {
                    userService.downloadCount(1);
                    System.out.println(1);
          }
 
          @Test
          @Rollback(false)
          public void testAddOpinion2() {
                    userService.downloadCount(2);
                    System.out.println(2);
          }
}

@TransactionConfiguration(transactionManager="transactionManager") 讀取Spring配置文件中名爲transactionManager的事務配置,defaultRollback爲事務回滾默認設置。該註解是可選的, 可以使用@Transactional與@Rollback配合完成事務管理。固然也可使用@Transactional與 @TransactionConfiguration配合。

@Transactional開啓事務。可放到類或方法上,類上做用於全部方法。

@Rollback事務回滾配置。只能放到方法上。

3)繼承AbstractTransactionalJUnit4SpringContextTests

package com.test;
 
import javax.annotation.Resource;
 
import org.junit.Test;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.transaction.TransactionConfiguration;
 
@ContextConfiguration(locations = { "classpath:config/applicationContext-*.xml", "classpath:services/ext/service-*.xml" })
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = false)
public class UserServiceTest extends AbstractTransactionalJUnit4SpringContextTests {
 
          @Resource
          private IUserService userService;
 
          @Test
          public void testAddOpinion1() {
                    userService.downloadCount(1);
                    System.out.println(1);
          }
 
          @Test
          public void testAddOpinion2() {
                    userService.downloadCount(2);
                    System.out.println(2);
          }
}

AbstractTransactionalJUnit4SpringContextTests: 這個類爲咱們解決了在web.xml中配置OpenSessionInview所解決的session生命週期延長的問題,因此要繼承這個類。該類已經在 類級別預先配置了好了事物支持,所以沒必要再配置@Transactional和@RunWith

4)繼承

package com.test;
 
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.transaction.TransactionConfiguration;
 
@ContextConfiguration(locations = { "classpath:config/applicationContext-*.xml", "classpath:services/ext/service-*.xml" })
@TransactionConfiguration(transactionManager = "transactionManager")
public class BaseTestCase extends AbstractTransactionalJUnit4SpringContextTests {
 
}
package com.test;
 
import javax.annotation.Resource;
 
import org.junit.Test;
import org.springframework.test.annotation.Rollback;
 
public class UserServiceTest extends BaseTestCase {
 
          @Resource
          private IUserService userService;
 
          @Test
          public void testAddOpinion1() {
                    userService.downloadCount(1);
                    System.out.println(1);
          }
 
          @Test
          @Rollback(false)
          public void testAddOpinion2() {
                    userService.downloadCount(2);
                    System.out.println(2);
          }
}

5)綜合

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TransactionConfiguration
@Transactional
public class PersonDaoTransactionUnitTest extends AbstractTransactionalJUnit4SpringContextTests {
 
          final Logger logger = LoggerFactory.getLogger(PersonDaoTransactionUnitTest.class);
 
          protected static int SIZE = 2;
          protected static Integer ID = new Integer(1);
          protected static String FIRST_NAME = "Joe";
          protected static String LAST_NAME = "Smith";
          protected static String CHANGED_LAST_NAME = "Jackson";
 
          @Autowired
          protected PersonDao personDao = null;
 
          /**
           * Tests that the size and first record match what is expected before the transaction.
           */
          @BeforeTransaction
          public void beforeTransaction() {
                    testPerson(true, LAST_NAME);
          }
 
          /**
           * Tests person table and changes the first records last name.
           */
          @Test
          public void testHibernateTemplate() throws SQLException {
                    assertNotNull("Person DAO is null.", personDao);
 
                    Collection<Person> lPersons = personDao.findPersons();
 
                    assertNotNull("Person list is null.", lPersons);
                    assertEquals("Number of persons should be " + SIZE + ".", SIZE, lPersons.size());
 
                    for (Person person : lPersons) {
                               assertNotNull("Person is null.", person);
 
                               if (ID.equals(person.getId())) {
                                         assertEquals("Person first name should be " + FIRST_NAME + ".", FIRST_NAME, person.getFirstName());
                                         assertEquals("Person last name should be " + LAST_NAME + ".", LAST_NAME, person.getLastName());
 
                                         person.setLastName(CHANGED_LAST_NAME);
 
                                         personDao.save(person);
                               }
                    }
          }
 
          /**
           * Tests that the size and first record match what is expected after the transaction.
           */
          @AfterTransaction
          public void afterTransaction() {
                    testPerson(false, LAST_NAME);
          }
 
          /**
           * Tests person table.
           */
          protected void testPerson(boolean beforeTransaction, String matchLastName) {
                    List<Map<String, Object>> lPersonMaps = simpleJdbcTemplate.queryForList("SELECT * FROM PERSON");
 
                    assertNotNull("Person list is null.", lPersonMaps);
                    assertEquals("Number of persons should be " + SIZE + ".", SIZE, lPersonMaps.size());
 
                    Map<String, Object> hPerson = lPersonMaps.get(0);
 
                    logger.debug((beforeTransaction ? "Before" : "After") + " transaction.  " + hPerson.toString());
 
                    Integer id = (Integer) hPerson.get("ID");
                    String firstName = (String) hPerson.get("FIRST_NAME");
                    String lastName = (String) hPerson.get("LAST_NAME");
 
                    if (ID.equals(id)) {
                               assertEquals("Person first name should be " + FIRST_NAME + ".", FIRST_NAME, firstName);
                               assertEquals("Person last name should be " + matchLastName + ".", matchLastName, lastName);
                    }
          }
 
}

@BeforeTransaction在事務以前執行

@AfterTransaction在事務以後執行

@NotTransactional不開啓事務

相關文章
相關標籤/搜索