Spring Boot使用單元測(cè)試 junit4

img

一俱两、前言

這次來介紹下Spring Boot中對(duì)單元測(cè)試的整合使用,本篇會(huì)通過以下4點(diǎn)來介紹宪彩,基本滿足日常需求

  • Service層單元測(cè)試
  • Controller層單元測(cè)試
  • 新斷言assertThat使用
  • 單元測(cè)試的回滾

二、正文

Spring Boot中引入單元測(cè)試很簡(jiǎn)單讲婚,依賴如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

本篇實(shí)例Spring Boot版本為1.5.9.RELEASE,引入spring-boot-starter-test后磺樱,有如下幾個(gè)庫:
? JUnit?—?The de-facto standard for unit testing Java applications.
? Spring Test & Spring Boot Test?—?Utilities and integration test support for Spring Boot applications.
? AssertJ?—?A fluent assertion library.
? Hamcrest?—?A library of matcher objects (also known as constraints or predicates).
? Mockito?—?A Java mocking framework.
? JSONassert?—?An assertion library for JSON.
? JsonPath?—?XPath for JSON.


image.png

三纳猫、Service單元測(cè)試

Spring Boot中單元測(cè)試類寫在在src/test/java目錄下,你可以手動(dòng)創(chuàng)建具體測(cè)試類竹捉,如果是IDEA芜辕,則可以通過IDEA自動(dòng)創(chuàng)建測(cè)試類块差,如下圖侵续,也可以通過快捷鍵??T(MAC)或者Ctrl+Shift+T(Window)來創(chuàng)建,如下:

image.png
image.png

自動(dòng)生成測(cè)試類如下:


img

然后再編寫創(chuàng)建好的測(cè)試類状蜗,具體代碼如下:

package com.dudu.service;
import com.dudu.domain.LearnResource;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.hamcrest.CoreMatchers.*;

@RunWith(SpringRunner.class)
@SpringBootTest
public class LearnServiceTest {

    @Autowired
    private LearnService learnService;
    
    @Test
    public void getLearn(){
        LearnResource learnResource=learnService.selectByKey(1001L);
        Assert.assertThat(learnResource.getAuthor(),is("嘟嘟MD獨(dú)立博客"));
    }
}

上面就是最簡(jiǎn)單的單元測(cè)試寫法,頂部只要@RunWith(SpringRunner.class)SpringBootTest即可鹉动,想要執(zhí)行的時(shí)候,鼠標(biāo)放在對(duì)應(yīng)的方法缸血,右鍵選擇run該方法即可飒炎。

測(cè)試用例中我使用了assertThat斷言笆豁,下文中會(huì)介紹,也推薦大家使用該斷言。

四哄孤、Controller單元測(cè)試

上面只是針對(duì)Service層做測(cè)試耕驰,但是有時(shí)候需要對(duì)Controller層(API)做測(cè)試,這時(shí)候就得用到MockMvc了录豺,你可以不必啟動(dòng)工程就能測(cè)試這些接口。

MockMvc實(shí)現(xiàn)了對(duì)Http請(qǐng)求的模擬饭弓,能夠直接使用網(wǎng)絡(luò)的形式双饥,轉(zhuǎn)換到Controller的調(diào)用,這樣可以使得測(cè)試速度快弟断、不依賴網(wǎng)絡(luò)環(huán)境咏花,而且提供了一套驗(yàn)證的工具,這樣可以使得請(qǐng)求的驗(yàn)證統(tǒng)一而且很方便阀趴。

Controller類:

package com.dudu.controller;

/** 教程頁面
 */
@Controller
@RequestMapping("/learn")
public class LearnController  extends AbstractController{
    @Autowired
    private LearnService learnService;
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @RequestMapping("")
    public String learn(Model model){
        model.addAttribute("ctx", getContextPath()+"/");
        return "learn-resource";
    }

    /**
     * 查詢教程列表
     * @param page
     * @return
     */
    @RequestMapping(value = "/queryLeanList",method = RequestMethod.POST)
    @ResponseBody
    public AjaxObject queryLearnList(Page<LeanQueryLeanListReq> page){
        List<LearnResource> learnList=learnService.queryLearnResouceList(page);
        PageInfo<LearnResource> pageInfo =new PageInfo<LearnResource>(learnList);
        return AjaxObject.ok().put("page", pageInfo);
    }
    
    /**
     * 新添教程
     * @param learn
     */
    @RequestMapping(value = "/add",method = RequestMethod.POST)
    @ResponseBody
    public AjaxObject addLearn(@RequestBody LearnResource learn){
        learnService.save(learn);
        return AjaxObject.ok();
    }

    /**
     * 修改教程
     * @param learn
     */
    @RequestMapping(value = "/update",method = RequestMethod.POST)
    @ResponseBody
    public AjaxObject updateLearn(@RequestBody LearnResource learn){
        learnService.updateNotNull(learn);
        return AjaxObject.ok();
    }

    /**
     * 刪除教程
     * @param ids
     */
    @RequestMapping(value="/delete",method = RequestMethod.POST)
    @ResponseBody
    public AjaxObject deleteLearn(@RequestBody Long[] ids){
        learnService.deleteBatch(ids);
        return AjaxObject.ok();
    }

    /**
     * 獲取教程
     * @param id
     */
    @RequestMapping(value="/resource/{id}",method = RequestMethod.GET)
    @ResponseBody
    public LearnResource qryLearn(@PathVariable(value = "id") Long id){
       LearnResource lean= learnService.selectByKey(id);
        return lean;
    }
}

里我們也自動(dòng)創(chuàng)建一個(gè)Controller的測(cè)試類昏翰,具體代碼如下:

package com.dudu.controller;

import com.dudu.domain.User;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringRunner.class)
@SpringBootTest

public class LearnControllerTest {
    @Autowired
    private WebApplicationContext wac;

    private MockMvc mvc;
    private MockHttpSession session;


    @Before
    public void setupMockMvc(){
        mvc = MockMvcBuilders.webAppContextSetup(wac).build(); //初始化MockMvc對(duì)象
        session = new MockHttpSession();
        User user =new User("root","root");
        session.setAttribute("user",user); //攔截器那邊會(huì)判斷用戶是否登錄,所以這里注入一個(gè)用戶
    }

    /**
     * 新增教程測(cè)試用例
     * @throws Exception
     */
    @Test
    public void addLearn() throws Exception{
        String json="{\"author\":\"HAHAHAA\",\"title\":\"Spring\",\"url\":\"http://tengj.top/\"}";
        mvc.perform(MockMvcRequestBuilders.post("/learn/add")
                    .accept(MediaType.APPLICATION_JSON_UTF8)
                    .content(json.getBytes()) //傳json參數(shù)
                    .session(session)
            )
           .andExpect(MockMvcResultMatchers.status().isOk())
           .andDo(MockMvcResultHandlers.print());
    }

    /**
     * 獲取教程測(cè)試用例
     * @throws Exception
     */
    @Test
    public void qryLearn() throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/learn/resource/1001")
                    .contentType(MediaType.APPLICATION_JSON_UTF8)
                    .accept(MediaType.APPLICATION_JSON_UTF8)
                    .session(session)
            )
           .andExpect(MockMvcResultMatchers.status().isOk())
           .andExpect(MockMvcResultMatchers.jsonPath("$.author").value("嘟嘟MD獨(dú)立博客"))
           .andExpect(MockMvcResultMatchers.jsonPath("$.title").value("Spring Boot干貨系列"))
           .andDo(MockMvcResultHandlers.print());
    }

    /**
     * 修改教程測(cè)試用例
     * @throws Exception
     */
    @Test
    public void updateLearn() throws Exception{
        String json="{\"author\":\"測(cè)試修改\",\"id\":1031,\"title\":\"Spring Boot干貨系列\(zhòng)",\"url\":\"http://tengj.top/\"}";
        mvc.perform(MockMvcRequestBuilders.post("/learn/update")
                .accept(MediaType.APPLICATION_JSON_UTF8)
                .content(json.getBytes())//傳json參數(shù)
                .session(session)
        )
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print());
    }

    /**
     * 刪除教程測(cè)試用例
     * @throws Exception
     */
    @Test
    public void deleteLearn() throws Exception{
        String json="[1031]";
        mvc.perform(MockMvcRequestBuilders.post("/learn/delete")
                .accept(MediaType.APPLICATION_JSON_UTF8)
                .content(json.getBytes())//傳json參數(shù)
                .session(session)
        )
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print());
    }

}

上面實(shí)現(xiàn)了基本的增刪改查的測(cè)試用例刘急,使用MockMvc的時(shí)候需要先用MockMvcBuilders使用構(gòu)建MockMvc對(duì)象棚菊,如下:

@Before
public void setupMockMvc(){
    mvc = MockMvcBuilders.webAppContextSetup(wac).build(); //初始化MockMvc對(duì)象
    session = new MockHttpSession();
    User user =new User("root","root");
    session.setAttribute("user",user); //攔截器那邊會(huì)判斷用戶是否登錄,所以這里注入一個(gè)用戶
}

因?yàn)閿r截器那邊會(huì)判斷是否登錄叔汁,所以這里我注入了一個(gè)用戶统求,你也可以直接修改攔截器取消驗(yàn)證用戶登錄,先測(cè)試完再開啟据块。

這里拿一個(gè)例子來介紹一下MockMvc簡(jiǎn)單的方法:

/**
 * 獲取教程測(cè)試用例
 * @throws Exception
 */
@Test
public void qryLearn() throws Exception {
    mvc.perform(MockMvcRequestBuilders.get("/learn/resource/1001")
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .accept(MediaType.APPLICATION_JSON_UTF8)
                .session(session)
        )
       .andExpect(MockMvcResultMatchers.status().isOk())
       .andExpect(MockMvcResultMatchers.jsonPath("$.author").value("嘟嘟MD獨(dú)立博客"))
       .andExpect(MockMvcResultMatchers.jsonPath("$.title").value("Spring Boot干貨系列"))
       .andDo(MockMvcResultHandlers.print());
}

mockMvc.perform執(zhí)行一個(gè)請(qǐng)求
MockMvcRequestBuilders.get(“/user/1”)構(gòu)造一個(gè)請(qǐng)求码邻,Post請(qǐng)求就用.post方法
contentType(MediaType.APPLICATION_JSON_UTF8)代表發(fā)送端發(fā)送的數(shù)據(jù)格式是application/json;charset=UTF-8
accept(MediaType.APPLICATION_JSON_UTF8)代表客戶端希望接受的數(shù)據(jù)類型為application/json;charset=UTF-8
session(session)注入一個(gè)session,這樣攔截器才可以通過
ResultActions.andExpect添加執(zhí)行完成后的斷言
ResultActions.andExpect(MockMvcResultMatchers.status().isOk())方法看請(qǐng)求的狀態(tài)響應(yīng)碼是否為200如果不是則拋異常另假,測(cè)試不通過
andExpect(MockMvcResultMatchers.jsonPath(“$.author”).value(“嘟嘟MD獨(dú)立博客”))這里jsonPath用來獲取author字段比對(duì)是否為嘟嘟MD獨(dú)立博客,不是就測(cè)試不通過
ResultActions.andDo添加一個(gè)結(jié)果處理器像屋,表示要對(duì)結(jié)果做點(diǎn)什么事情,比如此處使用MockMvcResultHandlers.print()輸出整個(gè)響應(yīng)結(jié)果信息
本例子測(cè)試如下:


image.png

mockMvc 更多例子可以本篇下方參考查看

五边篮、新斷言assertThat使用

JUnit 4.4 結(jié)合 Hamcrest 提供了一個(gè)全新的斷言語法——assertThat己莺。程序員可以只使用 assertThat 一個(gè)斷言語句奏甫,結(jié)合 Hamcrest 提供的匹配符,就可以表達(dá)全部的測(cè)試思想篇恒,我們引入的版本是Junit4.12所以支持assertThat逆害。

assertThat 的基本語法如下:

清單 1 assertThat 基本語法

assertThat( [value], [matcher statement] );
  • value 是接下來想要測(cè)試的變量值;
  • matcher statement 是使用 Hamcrest 匹配符來表達(dá)的對(duì)前面變量所期望的值的聲明种远,如果 value 值與 matcher statement 所表達(dá)的期望值相符腋么,則測(cè)試成功,否則測(cè)試失敗腾么。

assertThat 的優(yōu)點(diǎn)

  • 優(yōu)點(diǎn) 1:以前 JUnit 提供了很多的 assertion 語句奈梳,如:assertEquals,assertNotSame解虱,assertFalse攘须,assertTrue,assertNotNull殴泰,assertNull 等于宙,現(xiàn)在有了 JUnit 4.4,一條 assertThat 即可以替代所有的 assertion 語句悍汛,這樣可以在所有的單元測(cè)試中只使用一個(gè)斷言方法捞魁,使得編寫測(cè)試用例變得簡(jiǎn)單,代碼風(fēng)格變得統(tǒng)一离咐,測(cè)試代碼也更容易維護(hù)谱俭。
  • 優(yōu)點(diǎn) 2:assertThat 使用了 Hamcrest 的 Matcher 匹配符,用戶可以使用匹配符規(guī)定的匹配準(zhǔn)則精確的指定一些想設(shè)定滿足的條件宵蛀,具有很強(qiáng)的易讀性昆著,而且使用起來更加靈活。如清單 2 所示:
    清單 2 使用匹配符 Matcher 和不使用之間的比較
// 想判斷某個(gè)字符串 s 是否含有子字符串 "developer" 或 "Works" 中間的一個(gè)
// JUnit 4.4 以前的版本:assertTrue(s.indexOf("developer")>-1||s.indexOf("Works")>-1 );
// JUnit 4.4:
assertThat(s, anyOf(containsString("developer"), containsString("Works"))); 
// 匹配符 anyOf 表示任何一個(gè)條件滿足則成立术陶,類似于邏輯或 "||"凑懂, 匹配符 containsString 表示是否含有參數(shù)子 
// 字符串,文章接下來會(huì)對(duì)匹配符進(jìn)行具體介紹
  • 優(yōu)點(diǎn) 3:assertThat 不再像 assertEquals 那樣梧宫,使用比較難懂的“謂賓主”語法模式(如:assertEquals(3, x);)征候,相反,assertThat 使用了類似于“主謂賓”的易讀語法模式(如:assertThat(x,is(3));)祟敛,使得代碼更加直觀疤坝、易讀。

  • 優(yōu)點(diǎn) 4:可以將這些 Matcher 匹配符聯(lián)合起來靈活使用馆铁,達(dá)到更多目的跑揉。如清單 3 所示:
    清單 3 Matcher 匹配符聯(lián)合使用

// 聯(lián)合匹配符not和equalTo表示“不等于”
assertThat( something, not( equalTo( "developer" ) ) ); 
// 聯(lián)合匹配符not和containsString表示“不包含子字符串”
assertThat( something, not( containsString( "Works" ) ) ); 
// 聯(lián)合匹配符anyOf和containsString表示“包含任何一個(gè)子字符串”
assertThat(something, anyOf(containsString("developer"), containsString("Works")));
  • 優(yōu)點(diǎn) 5:錯(cuò)誤信息更加易懂、可讀且具有描述性(descriptive)
    JUnit 4.4 以前的版本默認(rèn)出錯(cuò)后不會(huì)拋出額外提示信息,如:
assertTrue( s.indexOf("developer") > -1 || s.indexOf("Works") > -1 );

如果該斷言出錯(cuò)历谍,只會(huì)拋出無用的錯(cuò)誤信息现拒,如:junit.framework.AssertionFailedError:null。
如果想在出錯(cuò)時(shí)想打印出一些有用的提示信息望侈,必須得程序員另外手動(dòng)寫印蔬,如:

assertTrue( "Expected a string containing 'developer' or 'Works'", 
    s.indexOf("developer") > -1 || s.indexOf("Works") > -1 );

非常的不方便,而且需要額外代碼脱衙。
JUnit 4.4 會(huì)默認(rèn)自動(dòng)提供一些可讀的描述信息侥猬,如清單 4 所示:
清單 4 JUnit 4.4 默認(rèn)提供一些可讀的描述性錯(cuò)誤信息

String s = "hello world!"; 
assertThat( s, anyOf( containsString("developer"), containsString("Works") ) ); 
// 如果出錯(cuò)后,系統(tǒng)會(huì)自動(dòng)拋出以下提示信息:
java.lang.AssertionError: 
Expected: (a string containing "developer" or a string containing "Works") 
got: "hello world!"

如何使用 assertThat
JUnit 4.4 自帶了一些 Hamcrest 的匹配符 Matcher捐韩,但是只有有限的幾個(gè)退唠,在類 org.hamcrest.CoreMatchers 中定義,要想使用他們荤胁,必須導(dǎo)入包 org.hamcrest.CoreMatchers.*瞧预。

清單 5 列舉了大部分 assertThat 的使用例子:

字符相關(guān)匹配符
/**equalTo匹配符斷言被測(cè)的testedValue等于expectedValue,
* equalTo可以斷言數(shù)值之間仅政,字符串之間和對(duì)象之間是否相等垢油,相當(dāng)于Object的equals方法
*/
assertThat(testedValue, equalTo(expectedValue));
/**equalToIgnoringCase匹配符斷言被測(cè)的字符串testedString
*在忽略大小寫的情況下等于expectedString
*/
assertThat(testedString, equalToIgnoringCase(expectedString));
/**equalToIgnoringWhiteSpace匹配符斷言被測(cè)的字符串testedString
*在忽略頭尾的任意個(gè)空格的情況下等于expectedString,
*注意:字符串中的空格不能被忽略
*/
assertThat(testedString, equalToIgnoringWhiteSpace(expectedString);
/**containsString匹配符斷言被測(cè)的字符串testedString包含子字符串subString**/
assertThat(testedString, containsString(subString) );
/**endsWith匹配符斷言被測(cè)的字符串testedString以子字符串suffix結(jié)尾*/
assertThat(testedString, endsWith(suffix));
/**startsWith匹配符斷言被測(cè)的字符串testedString以子字符串prefix開始*/
assertThat(testedString, startsWith(prefix));
一般匹配符
/**nullValue()匹配符斷言被測(cè)object的值為null*/
assertThat(object,nullValue());
/**notNullValue()匹配符斷言被測(cè)object的值不為null*/
assertThat(object,notNullValue());
/**is匹配符斷言被測(cè)的object等于后面給出匹配表達(dá)式*/
assertThat(testedString, is(equalTo(expectedValue)));
/**is匹配符簡(jiǎn)寫應(yīng)用之一圆丹,is(equalTo(x))的簡(jiǎn)寫秸苗,斷言testedValue等于expectedValue*/
assertThat(testedValue, is(expectedValue));
/**is匹配符簡(jiǎn)寫應(yīng)用之二,is(instanceOf(SomeClass.class))的簡(jiǎn)寫运褪,
*斷言testedObject為Cheddar的實(shí)例
*/
assertThat(testedObject, is(Cheddar.class));
/**not匹配符和is匹配符正好相反,斷言被測(cè)的object不等于后面給出的object*/
assertThat(testedString, not(expectedString));
/**allOf匹配符斷言符合所有條件玖瘸,相當(dāng)于“與”(&&)*/
assertThat(testedNumber, allOf( greaterThan(8), lessThan(16) ) );
/**anyOf匹配符斷言符合條件之一秸讹,相當(dāng)于“或”(||)*/
assertThat(testedNumber, anyOf( greaterThan(16), lessThan(8) ) );
數(shù)值相關(guān)匹配符
/**closeTo匹配符斷言被測(cè)的浮點(diǎn)型數(shù)testedDouble在20.0?à0.5范圍之內(nèi)*/
assertThat(testedDouble, closeTo( 20.0, 0.5 ));
/**greaterThan匹配符斷言被測(cè)的數(shù)值testedNumber大于16.0*/
assertThat(testedNumber, greaterThan(16.0));
/** lessThan匹配符斷言被測(cè)的數(shù)值testedNumber小于16.0*/
assertThat(testedNumber, lessThan (16.0));
/** greaterThanOrEqualTo匹配符斷言被測(cè)的數(shù)值testedNumber大于等于16.0*/
assertThat(testedNumber, greaterThanOrEqualTo (16.0));
/** lessThanOrEqualTo匹配符斷言被測(cè)的testedNumber小于等于16.0*/
assertThat(testedNumber, lessThanOrEqualTo (16.0));
集合相關(guān)匹配符
/**hasEntry匹配符斷言被測(cè)的Map對(duì)象mapObject含有一個(gè)鍵值為"key"對(duì)應(yīng)元素值為"value"的Entry項(xiàng)*/
assertThat(mapObject, hasEntry("key", "value" ) );
/**hasItem匹配符表明被測(cè)的迭代對(duì)象iterableObject含有元素element項(xiàng)則測(cè)試通過*/
assertThat(iterableObject, hasItem (element));
/** hasKey匹配符斷言被測(cè)的Map對(duì)象mapObject含有鍵值“key”*/
assertThat(mapObject, hasKey ("key"));
/** hasValue匹配符斷言被測(cè)的Map對(duì)象mapObject含有元素值value*/
assertThat(mapObject, hasValue(value));

** 五、單元測(cè)試回滾
單元個(gè)測(cè)試的時(shí)候如果不想造成垃圾數(shù)據(jù)雅倒,可以開啟事物功能璃诀,記在方法或者類頭部添加@Transactional注解即可,如下:

@Test
@Transactional
public void add(){
    LearnResource bean = new LearnResource();
    bean.setAuthor("測(cè)試回滾");
    bean.setTitle("回滾用例");
    bean.setUrl("http://tengj.top");
    learnService.save(bean);
}

這樣測(cè)試完數(shù)據(jù)就會(huì)回滾了,不會(huì)造成垃圾數(shù)據(jù)蔑匣。如果你想關(guān)閉回滾劣欢,只要加上@Rollback(false)注解即可。@Rollback表示事務(wù)執(zhí)行完回滾裁良,支持傳入一個(gè)參數(shù)value凿将,默認(rèn)true即回滾,false不回滾价脾。

如果你使用的數(shù)據(jù)庫是Mysql牧抵,有時(shí)候會(huì)發(fā)現(xiàn)加了注解@Transactional 也不會(huì)回滾,那么你就要查看一下你的默認(rèn)引擎是不是InnoDB,如果不是就要改成InnoDB犀变。

MyISAM與InnoDB是mysql目前比較常用的兩個(gè)數(shù)據(jù)庫存儲(chǔ)引擎妹孙,MyISAM與InnoDB的主要的不同點(diǎn)在于性能和事務(wù)控制上。這里簡(jiǎn)單的介紹一下兩者間的區(qū)別和轉(zhuǎn)換方法:

  • MyISAM:MyISAM是MySQL5.5之前版本默認(rèn)的數(shù)據(jù)庫存儲(chǔ)引擎获枝。MYISAM提供高速存儲(chǔ)和檢索蠢正,以及全文搜索能力,適合數(shù)據(jù)倉庫等查詢頻繁的應(yīng)用省店。但不支持事務(wù)嚣崭、也不支持外鍵。MyISAM格式的一個(gè)重要缺陷就是不能在表損壞后恢復(fù)數(shù)據(jù)萨西。

  • InnoDB:InnoDB是MySQL5.5版本的默認(rèn)數(shù)據(jù)庫存儲(chǔ)引擎有鹿,不過InnoDB已被Oracle收購,MySQL自行開發(fā)的新存儲(chǔ)引擎Falcon將在MySQL6.0版本引進(jìn)谎脯。InnoDB具有提交葱跋、回滾和崩潰恢復(fù)能力的事務(wù)安全。但是比起MyISAM存儲(chǔ)引擎源梭,InnoDB寫的處理效率差一些并且會(huì)占用更多的磁盤空間以保留數(shù)據(jù)和索引娱俺。盡管如此,但是InnoDB包括了對(duì)事務(wù)處理和外來鍵的支持废麻,這兩點(diǎn)都是MyISAM引擎所沒有的荠卷。

  • MyISAM適合:(1)做很多count 的計(jì)算;(2)插入不頻繁烛愧,查詢非常頻繁油宜;(3)沒有事務(wù)。

  • InnoDB適合:(1)可靠性要求比較高怜姿,或者要求事務(wù)慎冤;(2)表更新和查詢都相當(dāng)?shù)念l繁,并且表鎖定的機(jī)會(huì)比較大的情況沧卢。(4)性能較好的服務(wù)器蚁堤,比如單獨(dú)的數(shù)據(jù)庫服務(wù)器,像阿里云的關(guān)系型數(shù)據(jù)庫RDS就推薦使用InnoDB引擎但狭。

修改默認(rèn)引擎的步驟
查看MySQL當(dāng)前默認(rèn)的存儲(chǔ)引擎:

mysql> show variables like '%storage_engine%';

你要看user表用了什么引擎(在顯示結(jié)果里參數(shù)engine后面的就表示該表當(dāng)前用的存儲(chǔ)引擎):

mysql> show create table user;

將user表修為InnoDB存儲(chǔ)引擎(也可以此命令將InnoDB換為MyISAM):

mysql> ALTER TABLE user ENGINE=INNODB;

如果要更改整個(gè)數(shù)據(jù)庫表的存儲(chǔ)引擎披诗,一般要一個(gè)表一個(gè)表的修改,比較繁瑣立磁,可以采用先把數(shù)據(jù)庫導(dǎo)出呈队,得到SQL,把MyISAM全部替換為INNODB唱歧,再導(dǎo)入數(shù)據(jù)庫的方式掂咒。
轉(zhuǎn)換完畢后重啟mysql

service mysqld restart

總結(jié)
到此為止,Spring Boot整合單元測(cè)試就基本完結(jié),關(guān)于MockMvc以及assertThat的用法大家可以繼續(xù)深入研究绍刮。后續(xù)會(huì)整合Swagger UI這個(gè)API文檔工具温圆,即提供API文檔又提供測(cè)試接口界面,相當(dāng)好用孩革。

參考

Junit學(xué)習(xí)筆記之五:MockMVC
探索 JUnit 4.4 新特性

源碼下載

[相關(guān)示例完整代碼]

來源

嘟嘟獨(dú)立博客

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末岁歉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子膝蜈,更是在濱河造成了極大的恐慌锅移,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件饱搏,死亡現(xiàn)場(chǎng)離奇詭異非剃,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)推沸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門备绽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鬓催,你說我怎么就攤上這事肺素。” “怎么了宇驾?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵倍靡,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我课舍,道長(zhǎng)塌西,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任筝尾,我火速辦了婚禮捡需,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘忿等。我一直安慰自己,他們只是感情好崔挖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布贸街。 她就那樣靜靜地躺著,像睡著了一般狸相。 火紅的嫁衣襯著肌膚如雪薛匪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天脓鹃,我揣著相機(jī)與錄音逸尖,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛娇跟,可吹牛的內(nèi)容都是我干的岩齿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼苞俘,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼盹沈!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起吃谣,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤乞封,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后岗憋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肃晚,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年仔戈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了关串。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡杂穷,死狀恐怖悍缠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情耐量,我是刑警寧澤飞蚓,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站廊蜒,受9級(jí)特大地震影響趴拧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜山叮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一著榴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧屁倔,春花似錦脑又、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至钞翔,卻和暖如春严卖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背布轿。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工哮笆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留来颤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓稠肘,卻偏偏與公主長(zhǎng)得像福铅,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子启具,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353