Android單元測試

一.基本介紹

背景:

目前處于高速迭代開發(fā)中的Android項目往往需要除黑盒測試外更加可靠的質(zhì)量保障霹俺,這正是單元測試的用武之地敬特。單元測試周期性對項目進行函數(shù)級別的測試掰邢,在良好的覆蓋率下,能夠持續(xù)維護代碼邏輯伟阔,從而支持項目從容應(yīng)對快速的版本更新辣之。

正是由于測試在開發(fā)中的重要地位,才會在IT界刮起了 TDD 的旋風(fēng)皱炉。TDD怀估,也就是測試驅(qū)動開發(fā)模式。它旨在強調(diào)在開發(fā)功能代碼之前合搅,先編寫測試代碼奏夫。也就是說在明確要開發(fā)某個功能后,首先思考如何對這個功能進行測試历筝,并完成測試代碼的編寫,然后編寫相關(guān)的代碼滿足這些測試用例廊谓。然后循環(huán)進行添加其他功能梳猪,直到完成全部功能的開發(fā)。

二.Java 測試工具(框架)

1.JUnit(推薦使用JUnit4)
JUnit 在日常開發(fā)中還是很常用的蒸痹,而且 Java 的各種 IDE (Eclipse春弥、MyEclipse、IntelliJ IDEA)都集成了 JUnit 的組件叠荠。當然匿沛,自己添加插件也是很方便的。JUnit 框架是 Java 語言單元測試當前的一站式解決方案榛鼎。這個框架值得稱贊逃呼,因為它把測試驅(qū)動的開發(fā)思想介紹給 Java 開發(fā)人員并教給他們?nèi)绾斡行У鼐帉憜卧獪y試。

2.TestNG
TestNG者娱,即Testing Next Generation抡笼,下一代測試技術(shù)。是根據(jù)JUnit和NUnit思想黄鳍,采用 jdk 的 annotation 技術(shù)來強化測試功能并借助XML 文件強化測試組織結(jié)構(gòu)而構(gòu)建的測試框架推姻。TestNG 的強大之處還在于不僅可以用來做單元測試,還可以用來做集成測試框沟。

重點介紹下JUnit4

JUnit是Java單元測試框架藏古,已經(jīng)在Eclipse中默認安裝增炭。目前主流的有JUnit3和JUnit4。JUnit3中拧晕,測試用例需要繼承TestCase類隙姿。JUnit4中,測試用例無需繼承TestCase類防症,只需要使用@Test等注解,建議使用JUnit4孟辑。

JUnit4通過注解的方式來識別測試方法。目前支持的主要注解有:

  • @BeforeClass 全局只會執(zhí)行一次蔫敲,而且是第一個運行
  • @Before 在測試方法運行之前運行
  • @Test 測試方法
  • @After 在測試方法運行之后允許
  • @AfterClass 全局只會執(zhí)行一次饲嗽,而且是最后一個運行
  • @Ignore 忽略此方法

@Before 該方法在每次測試方法調(diào)用前都會調(diào)用 @Test 說明了該方法需要測試 @BeforeClass 該方法在所有測試方法之前調(diào)用,只會被調(diào)用一次 @After 該方法在每次測試方法調(diào)用后都會調(diào)用 @AfterClass 該方法在所有測試方法之后調(diào)用奈嘿,只會被調(diào)用一次 @Ignore 忽略該方法

三.單元測試范圍

一般來說貌虾,單元測試任務(wù)包括

  1. 接口功能測試:用來保證接口功能的正確性。
  2. 局部數(shù)據(jù)結(jié)構(gòu)測試(不常用):用來保證接口中的數(shù)據(jù)結(jié)構(gòu)是正確的裙犹。 比如(1).變量有無初始值,(2).變量是否溢出.
  3. 邊界條件測試
    (1).變量沒有賦值(即為NULL)
    (2).變量是數(shù)值(或字符)
    -主要邊界:最小值尽狠,最大值,無窮大(對于DOUBLE等)
    -溢出邊界(期望異骋镀裕或拒絕服務(wù)):最小值-1袄膏,最大值+1
    -臨近邊界:最小值+1,最大值-1
    (3). 變量是字符串
    -引用“字符變量”的邊界
    -空字符串
    -對字符串長度應(yīng)用“數(shù)值變量”的邊界
    (4).變量是集合
    -空集合
    -對集合的大小應(yīng)用“數(shù)值變量”的邊界
    -調(diào)整次序:升序掺冠、降序
    (5). 變量有規(guī)律
    -比如對于Math.sqrt沉馆,給出n2-1,和n2+1的邊界
    (6). 所有獨立執(zhí)行通路測試:保證每一條代碼德崭,每個分支都經(jīng)過測試
    -代碼覆蓋率
    1>.語句覆蓋:保證每一個語句都執(zhí)行到了
    2>.判定覆蓋(分支覆蓋):保證每一個分支都執(zhí)行到
    3>.條件覆蓋:保證每一個條件都覆蓋到true和false(即if斥黑、while中的條件語句)
    4>.路徑覆蓋:保證每一個路徑都覆蓋到

-相關(guān)軟件 (Cobertura:語句覆蓋)

  1. 各條錯誤處理通路測試:保證每一個異常都經(jīng)過測試

如下是一個JUnit4的示例:

/**
 * Created by huanming on 17/3/13.
 */
public class Junit4TestCase {

    @BeforeClass
    public static void setUpBeforeClass() {
        System.out.println("Set up before class");
    }

    @Before
    public void setUp() throws Exception {
        System.out.println("Set up");
    }

    @Test
    public void testMathPow() {
        System.out.println("Test Math.pow");
        Assert.assertEquals(4.0, Math.pow(2.0, 2.0), 0.0);
    }

    @Test
    public void testMathMin() {
        System.out.println("Test Math.min");
        Assert.assertEquals(2.0, Math.min(2.0, 4.0), 0.0);
    }

    // 期望此方法拋出NullPointerException異常
    @Test(expected = NullPointerException.class)
    public void testException() {
        System.out.println("Test exception");
        Object obj = null;
        obj.toString();
    }

    // 忽略此測試方法
    @Ignore
    @Test
    public void testMathMax() {
        Assert.fail("沒有實現(xiàn)");
    }
    // 使用“假設(shè)”來忽略測試方法
    @Test
    public void testAssume(){
        System.out.println("Test assume");
        // 當假設(shè)失敗時,則會停止運行眉厨,但這并不會意味測試方法失敗锌奴。
        Assume.assumeTrue(false);
        Assert.fail("沒有實現(xiàn)");
    }

    @After
    public void tearDown() throws Exception {
        System.out.println("Tear down");
    }

    @AfterClass
    public static void tearDownAfterClass() {
        System.out.println("Tear down After class");
    }

}

運行結(jié)果:


屏幕快照 2017-03-17 下午2.26.04.png

四. 單元測試框架>Robolectric

參考文章:
http://robolectric.org
https://github.com/robolectric/robolectric
https://en.wikipedia.org/wiki/Unit_testing
https://github.com/square/okhttp/tree/master/mockwebserver

  1. 介紹
    (1). Robolectric 是一個開源的framework,他們的做法是通過實現(xiàn)一套JVM能運行的Android代碼憾股,然后在unit test運行的時候去截取android相關(guān)的代碼調(diào)用鹿蜀,然后轉(zhuǎn)到他們的他們實現(xiàn)的代碼去執(zhí)行這個調(diào)用的過程。
    舉個例子說明一下服球,比如android里面有個類叫TextView耻姥,他們實現(xiàn)了一個類叫ShadowTextView。這個類基本上實現(xiàn)了TextView的所有公共接口有咨,假設(shè)你在unit test里面寫到
    String text = textView.getText().toString();琐簇。在這個unit test運行的時候,Robolectric會自動判斷你調(diào)用了Android相關(guān)的代碼textView.getText(),然后這個調(diào)用過程在底層截取了婉商,轉(zhuǎn)到ShadowTextViewgetText實現(xiàn)似忧。而ShadowTextView是真正實現(xiàn)了getText這個方法的,所以這個過程便可以正常執(zhí)行丈秩。
    (2). 除了實現(xiàn)Android里面的類的現(xiàn)有接口盯捌,Robolectric還做了另外一件事情,極大地方便了unit testing的工作蘑秽。那就是他們給每個Shadow類額外增加了很多接口饺著,可以讀取對應(yīng)的Android類的一些狀態(tài)。比如我們知道ImageView有一個方法叫setImageResource(resourceId)肠牲,然而并沒有一個對應(yīng)的getter方法叫g(shù)etImageResourceId()幼衰,這樣你是沒有辦法測試這個ImageView是不是顯示了你想要的image。而在Robolectric實現(xiàn)的對應(yīng)的ShadowImageView里面缀雳,則提供了getImageResourceId()這個接口渡嚣。你可以用來測試它是不是正確的顯示了你想要的Image.

  2. 環(huán)境配置
    Android單元測試依舊需要JUnit框架的支持,Robolectric只是提供了Android代碼的運行環(huán)境肥印。如果使用Robolectric 3.0识椰,依賴配置如下:

testCompile 'junit:junit:4.12'
    testCompile('org.robolectric:robolectric:3.0') {
        exclude module: 'commons-logging'
    }

Gradle對Robolectric 2.4的支持并不像3.0這樣好,但Robolectric 2.4所有的測試框架均在一個包里深碱,如果使用Robolectric 2.4腹鹉,則需要如下配置:

//這行配置在buildscript的dependencies中
classpath 'org.robolectric:robolectric-gradle-plugin:0.14.+'
apply plugin: 'robolectric'
androidTestCompile 'org.robolectric:robolectric:2.4'

需要注意:Android Studio小于2.0的版本,要支持單元測試需要設(shè)置“Build Variants”敷硅,路徑是“View -->Tool Windows-->Build Variants”种蘸,然后設(shè)置為“Unit Tests”;當版本為2.0時竞膳,默認就支持。

屏幕快照 2017-03-19 下午1.47.32.png
                           圖2 單元測試工程位置

如圖1所示的綠色文件夾即是單元測試工程诫硕。這些代碼能夠檢測目標代碼的正確性坦辟,打包時單元測試的代碼不會被編譯進入APK中。

Robolectric最麻煩就是下載依賴章办! 由于我們生活在天朝锉走,下載國外的依賴很慢,即使有了翻墻藕届,效果也一般挪蹭。

注意:第一次運行可能需要下載一些library,依賴庫休偶,可能需要花一點時間梁厉,這個跟unit test本身沒關(guān)。

第二種方法:maven地址指向 阿里云的地址。
build.gradle

allprojects {
        repositories {
            //依賴庫词顾,阿里云地址
            maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
            jcenter()
        }
    }

具體原理參考: http://www.reibang.com/p/a01628c3ea16

五.Robolectric使用介紹

Mock

參考文章:
http://www.open-open.com/lib/view/open1470724287040.html

配置:

testCompile 'org.mockito:mockito-core:1.9.5'

說白了就是打樁(Stub)或則模擬八秃,當你調(diào)用一個不好在測試中創(chuàng)建的對象時,Mock框架為你模擬一個和真實對象類似的替身來完成相應(yīng)的行為肉盹。
mock對象就是在調(diào)試期間用來作為真實對象的替代品昔驱。Mockito是Java中常見的Mock框架。

Robolectric在文檔中聲稱:“No Mocking Frameworks Required”:對于Robolectric的另一種可選方法是使用mock框架上忍,比如Mockito骤肛;或者模擬出Android SDK。雖然這是個有效的方法窍蓝,但基本上是應(yīng)用代碼的反向?qū)崿F(xiàn)腋颠。

Mockito雖然不能模擬final類、匿名類和Java基本類型它抱;對于final方法和static方法秕豫,不能對其 when(…).thenReturn(…) 操作。另外mock對象观蓄,大多都需要植入到應(yīng)用代碼中混移,從而進行verify(...)操作;但應(yīng)用代碼中不一定有相應(yīng)的set方法侮穿,如果要植入歌径,就需要為了測試添加應(yīng)用代碼。

但是亲茅, Mockito + Powermock可以解決上述的問題回铛。

示例:

@Implements(HttpClient.class)
public class ShadowHttpClient {

    protected static boolean isHandleError = false;
    protected static boolean isRaiseException = false;
    public static String lastRequestPath;
    public static String lastRequestData;
    public static List<String> allExecutedAction = new ArrayList<String>();
    public static List<String> allRequestData = new ArrayList<String>();
    private static ResponseObjectConvert converter;
    private static List<HttpResponseResult> responseResultList;
    private static int position = 0;

    @RealObject
    HttpClient httpClient;

    public void __constructor__(String host, int port, boolean isEncryptionEnabled) {

    }

    @Implementation
    public HttpResponseResult sendRequestGetResponse(String path, String request) {
        lastRequestPath = path;
        lastRequestData = request;
        allExecutedAction.add(path);
        allRequestData.add(request);
        if (isRaiseException) {
            throw new RuntimeException();
        }

        if (converter != null) {
            if (isHandleError) {
                setResponseResultList(asList(new HttpResponseResult(FAILED, converter.convertResponse(), null)));
            } else {
                setResponseResultList(asList(new HttpResponseResult(SUCCEEDED, converter.convertResponse(), null)));
            }
        }

        return responseResultList.get(position++);
    }

    @Implementation
    public HttpResponseResult getResponse(String path) {
        return sendRequestGetResponse(path,"");
    }

    public static void reset() {
        lastRequestPath = null;
        lastRequestData = null;
        allExecutedAction.clear();
        allRequestData.clear();

        ShadowHttpClient.converter = null;
        ShadowHttpClient.responseResultList = null;
        ShadowHttpClient.isHandleError = false;
        ShadowHttpClient.isRaiseException = false;
    }

    public static void setRaiseException(boolean isRaiseException) {
        ShadowHttpClient.isRaiseException = isRaiseException;
    }

    public static void setConverter(ResponseObjectConvert converter) {
        ShadowHttpClient.converter = converter;
    }

    public static void setHandleError(boolean handleError) {
        ShadowHttpClient.isHandleError = handleError;
    }

    public static void setResponseResultList(List<HttpResponseResult> responseResultList) {
        position = 0;
        ShadowHttpClient.responseResultList = responseResultList;
    }

    public interface ResponseObjectConvert {
        public String convertResponse();
    }

Mock寫法介紹

對于一些依賴關(guān)系復(fù)雜的測試對象,可以采用Mock框架解除依賴克锣,常用的有Mockito茵肃。例如Mock一個List類型的對象實例,可以采用如下方式:

List list = mock(List.class);   //mock得到一個對象袭祟,也可以用@mock注入一個對象

所得到的list對象實例便是List類型的實例验残,如果不采用mock,List其實只是個接口巾乳,我們需要構(gòu)造或者借助ArrayList才能進行實例化您没。與Shadow不同,Mock構(gòu)造的是一個虛擬的對象胆绊,用于解耦真實對象所需要的依賴氨鹏。Mock得到的對象僅僅是具備測試對象的類型,并不是真實的對象压状,也就是并沒有執(zhí)行過真實對象的邏輯仆抵。
Mock也具備一些補充JUnit的驗證函數(shù),比如設(shè)置函數(shù)的執(zhí)行結(jié)果,示例如下:

When(sample.dosomething()).thenReturn(someAction);
//when(一個函數(shù)執(zhí)行).thenReturn(一個可替代真實函數(shù)的結(jié)果的返回值);
//上述代碼是設(shè)置sample.dosomething()的返回值肢础,當執(zhí)行了sample.dosomething()這個函數(shù)時还栓,
//就會得到someAction,從而解除了對真實的sample.dosomething()函數(shù)的依賴

上述代碼為被測函數(shù)定義一個可替代真實函數(shù)的結(jié)果的返回值传轰。當使用這個函數(shù)后剩盒,這個可驗證的結(jié)果便會產(chǎn)生影響,從而代替函數(shù)的真實結(jié)果慨蛙,這樣便解除了對真實函數(shù)的依賴辽聊。
同時Mock框架也可以驗證函數(shù)的執(zhí)行次數(shù),代碼如下:

List list = mock(List.class);   //Mock得到一個對象
list.add(1);                    //執(zhí)行一個函數(shù)
verify(list).add(1);            //驗證這個函數(shù)的執(zhí)行
verify(list,time(3)).add(1);    //驗證這個函數(shù)的執(zhí)行次數(shù)

在一些需要解除網(wǎng)絡(luò)依賴的場景中期贫,多使用Mock跟匆。比如對retrofit框架的網(wǎng)絡(luò)依賴解除如下:

public class MockClient implements Client {
    @Override
    public Response execute(Request request) throws IOException {
        Uri uri = Uri.parse(request.getUrl());
        String responseString = "";
        if(uri.getPath().equals("/path/of/interest")) {
            responseString = "返回的json1";//這里是設(shè)置返回值
        } else {
            responseString = "返回的json2";
        }
        return new Response(request.getUrl(), 200, "nothing", Collections.EMPTY_LIST, new TypedByteArray("application/json", responseString.getBytes()));
    }
}
//MockClient使用方式如下:
RestAdapter.Builder builder = new RestAdapter.Builder();
builder.setClient(new MockClient());

這種方式下retrofit的response可以由單元測試編寫者設(shè)置,而不來源于網(wǎng)絡(luò)通砍,從而解除了對網(wǎng)絡(luò)環(huán)境的依賴玛臂。

Shadow
Robolectric的本質(zhì)是在Java運行環(huán)境下,采用Shadow的方式對Android中的組件進行模擬測試封孙,從而實現(xiàn)Android單元測試迹冤。對于一些Robolectirc暫不支持的組件,可以采用自定義Shadow的方式擴展Robolectric的功能虎忌。

Robolectric定義了大量的Shadow類泡徙,修改或者擴展了Android OS類的行為。當一個Android OS類被實例化膜蠢,Robolectric會搜索相應(yīng)的Shadow類堪藐;如果找到了,將創(chuàng)建與之關(guān)聯(lián)的Shadow對象挑围。Android OS方法每次被調(diào)用時礁竞,Robolectirc確保:如果存在,Shadow類中的相應(yīng)方法先被調(diào)用杉辙,這樣就有機會做測試相關(guān)邏輯模捂。這種策略可運用于所有的方法,包括static和final方法奏瞬。

@Implements(Point.class)
public class ShadowPoint {
  @RealObject private Point realPoint;
  ...
  public void __constructor__(int x, int y) {
    realPoint.x = x;
    realPoint.y = y;
  }
}

上述實例中,@Implements是聲明Shadow的對象泉孩,@RealObject是獲取一個Android 對象硼端,constructor則是該Shadow的構(gòu)造函數(shù),Shadow還可以修改一些函數(shù)的功能寓搬,只需要在重載該函數(shù)的時候添加@Implementation珍昨,這種方式可以有效擴展Robolectric的功能。
Shadow是通過對真實的Android對象進行函數(shù)重載、初始化等方式對Android對象進行擴展镣典,Shadow出來的對象的功能接近Android對象兔毙,可以看成是對Android對象一種修復(fù)。自定義的Shadow需要在config中聲明兄春,聲明寫法是@Config(shadows=ShadowPoint.class)澎剥。

常見Robolectric用法

Robolectric支持單元測試范圍從Activity的跳轉(zhuǎn)、Activity展示View(包括菜單)和Fragment到View的點擊觸摸以及事件響應(yīng)赶舆,同時Robolectric也能測試Toast和Dialog哑姚。對于需要網(wǎng)絡(luò)請求數(shù)據(jù)的測試,Robolectric可以模擬網(wǎng)絡(luò)請求的response芜茵。對于一些Robolectric不能測試的對象叙量,比如ConcurrentTask,可以通過自定義Shadow的方式現(xiàn)實測試九串。下面將著重介紹Robolectric的常見用法绞佩。

  1. Activity展示測試與跳轉(zhuǎn)測試
    創(chuàng)建網(wǎng)絡(luò)請求后,便可以測試Activity了猪钮。測試代碼如下:
@Test
public void testSampleActivity(){
    SampleActivity sampleActivity=Robolectric.buildActivity(SampleActivity.class).
                create().resume().get();
    assertNotNull(sampleActivity);
    assertEquals("Activity的標題", sampleActivity.getTitle());
}

Robolectric.buildActivity()用于構(gòu)造Activity品山,create()函數(shù)執(zhí)行后,該Activity會運行到onCreate周期躬贡,resume()則對應(yīng)onResume周期谆奥。assertNotNull和assertEquals是JUnit中的斷言,Robolectric只提供運行環(huán)境拂玻,邏輯判斷還是需要依賴JUnit中的斷言酸些。
Activity跳轉(zhuǎn)是Android開發(fā)的重要邏輯,其測試方法如下:

 @Test
    public void testMainActivity() {
        MainActivity mainActivity = Robolectric.setupActivity(MainActivity.class);
        mainActivity.findViewById(R.id.textView1).performClick();

        Intent expectedIntent = new Intent(mainActivity, SecondActivity.class);
        ShadowActivity shadowActivity = Shadows.shadowOf(mainActivity);
        Intent actualIntent = shadowActivity.getNextStartedActivity();
        Assert.assertEquals(expectedIntent, actualIntent);
    }
  1. Dialog和Toast測試
    測試Dialog和Toast的方法如下:
public void testDialog(){
    Dialog dialog = ShadowDialog.getLatestDialog();
    assertNotNull(dialog);
}
public void testToast(String toastContent){
    ShadowHandler.idleMainLooper();
    assertEquals(toastContent, ShadowToast.getTextOfLatestToast());
}

上述函數(shù)均需要在Dialog或Toast產(chǎn)生之后執(zhí)行檐蚜,能夠測試Dialog和Toast是否彈出魄懂。

Fragment展示與切換
Fragment是Activity的一部分,在Robolectric模擬執(zhí)行Activity過程中闯第,如果觸發(fā)了被測試的代碼中的Fragment添加邏輯市栗,F(xiàn)ragment會被添加到Activity中。
需要注意Fragment出現(xiàn)的時機咳短,如果目標Activity中的Fragment的添加是執(zhí)行在onResume階段填帽,在Activity被Robolectric執(zhí)行resume()階段前,該Activity中并不會出現(xiàn)該Fragment咙好。采用Robolectric主動添加Fragment的方法如下:

@Test
public void addfragment(Activity activity, int fragmentContent){
    FragmentTestUtil.startFragment(activity.getSupportFragmentManager().findFragmentById(fragmentContent));
    Fragment fragment = activity.getSupportFragmentManager().findFragmentById(fragmentContent);
    assertNotNull(fragment);
}

startFragment()函數(shù)的主體便是常用的添加fragment的代碼篡腌。切換一個Fragment往往由Activity中的代碼邏輯完成,需要Activity的引用勾效。
控件的點擊以及可視驗證

@Test
public void testButtonClick(int buttonID){
    Button submitButton = (Button) activity.findViewById(buttonID);
    assertTrue(submitButton.isEnabled());
    submitButton.performClick();
    //驗證控件的行為
}

對控件的點擊驗證是調(diào)用performClick()嘹悼,然后斷言驗證其行為叛甫。對于ListView這類涉及到Adapter的控件的點擊驗證,寫法如下:

//listView被展示之后

listView.performItemClick(listView.getAdapter().getView(position, null, null), 0, 0);

與button等控件稍有不同杨伙。

六.Robolectric單元測試編寫結(jié)構(gòu)

如下實例:


未完待續(xù)......

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末其监,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子限匣,更是在濱河造成了極大的恐慌抖苦,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件膛腐,死亡現(xiàn)場離奇詭異睛约,居然都是意外死亡,警方通過查閱死者的電腦和手機哲身,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門辩涝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人勘天,你說我怎么就攤上這事怔揩。” “怎么了脯丝?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵商膊,是天一觀的道長。 經(jīng)常有香客問我宠进,道長晕拆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任材蹬,我火速辦了婚禮实幕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘堤器。我一直安慰自己昆庇,他們只是感情好,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布闸溃。 她就那樣靜靜地躺著整吆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辉川。 梳的紋絲不亂的頭發(fā)上表蝙,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機與錄音乓旗,去河邊找鬼府蛇。 笑死,一個胖子當著我的面吹牛寸齐,可吹牛的內(nèi)容都是我干的欲诺。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼渺鹦,長吁一口氣:“原來是場噩夢啊……” “哼扰法!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起毅厚,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤塞颁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后吸耿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體祠锣,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年咽安,在試婚紗的時候發(fā)現(xiàn)自己被綠了伴网。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡妆棒,死狀恐怖澡腾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情糕珊,我是刑警寧澤动分,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站红选,受9級特大地震影響澜公,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜喇肋,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一坟乾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧苟蹈,春花似錦糊渊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至菱鸥,卻和暖如春宗兼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背氮采。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工殷绍, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鹊漠。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓主到,卻偏偏與公主長得像茶行,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子登钥,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容

  • Android單元測試介紹 處于高速迭代開發(fā)中的Android項目往往需要除黑盒測試外更加可靠的質(zhì)量保障畔师,這正是單...
    東經(jīng)315度閱讀 3,104評論 6 37
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,072評論 25 707
  • 為什么要做單元測試 學(xué)習(xí)過或者了解軟件工程的人一定對這個東西不陌生,很多人也知道這個東西很重要牧牢,但是總是以各種借口...
    DanieX閱讀 624評論 0 3
  • 今天有客戶問我:有沒有聽說過九型人格看锉? 事實是聽過,但沒有太了解塔鳍。大致就是將人的性格分成九種伯铣,然后讓人對號入座等等...
    萍空間閱讀 193評論 0 0
  • 三月二十五日,春分后第五天轮纫,氣溫回升腔寡、陰雨轉(zhuǎn)晴。這日天緣茶室掌唾,有位新客到訪——覓仙泉蹬蚁。 覓仙泉是源自大別山深層巖石...
    小金瓜閱讀 846評論 0 3