doublex學(xué)習(xí)筆記

什么是doublex

官網(wǎng)上對于doublex的介紹很簡單,就是一個Test Double的Python實現(xiàn)框架

Powerful test doubles framework for Python

也可以理解為doublex是Python的一個庫,用于實現(xiàn)Test Stub/Spy/Mock。

關(guān)于Test Double(Stub/Spy/Mock)的理解,可以參考此篇文章七婴。

doublex的安裝

安裝有多種方式,pip依然是首選。

pip3 install doublex

doublex基本使用

doublex提供三種類型的Test Double嘹狞,分別是Stub & Mock & Spy。在介紹它們的具體使用方法之前誓竿,先簡單介紹下今天例子使用的SUT:

  1. SUT(system under test) - playerService
  2. DOC(depended-on component) - dataService, profileService, bodyService, salaryService

playerService接受球員名稱和年份磅网,然后返回一個球員的基本信息;這個基本信息由dataService球員數(shù)據(jù)和profileService球員概況組成筷屡,所以playerService依賴于dataService和profileService涧偷。playerService也返回由bodyService提供的身體指標,以及由salaryService支持的薪水服務(wù)毙死。

由于只是為了體現(xiàn)doublex使用方法的demo燎潮,實現(xiàn)非常簡單,都是hard code扼倘。具體請看github确封。

Test Stub - Stubs tell you what you wanna hear

doublex提供了Stub()對象來創(chuàng)建Test Stub,根據(jù)參數(shù)的不同分為 StubFree Stub

Stub

假設(shè)如果dataService對象就是我們要替換的依賴對象,Stub(dataService)帶有dataService對象為參數(shù)爪喘,那就代表這個Stub實例具有和dataService對象一樣的屬性颜曾,也就是說這個Stub實例可以“替換”dataService對象來與SUT進行交互了。

下面代碼展示了Stub()基本使用場景和方法

from unittest import TestCase, main
from doublex_demo.Service import dataService as ds
from doublex_demo.Service import profileService as pos
from doublex_demo.Service import playerService as pls
from doublex_demo.Service import bodyService as bs
from doublex import Stub, ANY_ARG, assert_that, is_

class TestStub(TestCase):

    def test_stub(self):

        playername = "Kawhi Leonard"

        #需使用with關(guān)鍵字來創(chuàng)建Stub
        #Stub接受dataService類對象作為參數(shù)秉剑,并且實現(xiàn)dataService類對象全部的方法
        #根據(jù)dataService的實現(xiàn)泛豪,get_assist()等方法不用接受參數(shù),這里的參數(shù)必須完全匹配
        #get_match_number()可以根據(jù)參數(shù)的不同返回不同的值
        #returns()方法定義返回值
        #不能定義非dataService的屬性
        with Stub(ds.dataService) as stub:
            stub.get_assist().returns("6")
            stub.get_score().returns("30")
            stub.get_rebound().returns("10")
            stub.get_match_number(2015).returns(playername + " plays 80 games at the year of 2015")
            stub.get_match_number(2016).returns(playername + " plays 81 games at the year of 2016")

        #使用來自于hamcrest的assert_that()和is_()做stub的驗證
        assert_that(stub.get_assist(), is_("6"))
        assert_that(stub.get_score(), is_("30"))
        assert_that(stub.get_rebound(), is_("10"))
        assert_that(stub.get_match_number(2015), is_("Kawhi Leonard plays 80 games at the year of 2015"))
        assert_that(stub.get_match_number(2016), is_("Kawhi Leonard plays 81 games at the year of 2016"))

        #使用stub代替dataService侦鹏,來對待測對象playerService進行測試驗證
        player_service_stub_2016 = pls.playerService(playername, 2016, stub, pos.profileService(playername), bs.bodyService(), ss.salaryService())
        assert_that(
            player_service_stub_2016.get_player_info().split('\n')[0],
            is_("Kawhi Leonard - san antonio spurs"))
        assert_that(
            player_service_stub_2016.get_player_info().split('\n')[-1],
            is_("Kawhi Leonard plays 81 games at the year of 2016"))

        player_service_stub_2015 = pls.playerService(playername, 2015, stub, pos.profileService(playername), bs.bodyService(), ss.salaryService())
        assert_that(
            player_service_stub_2015.get_player_info().split('\n')[-1],
            is_("Kawhi Leonard plays 80 games at the year of 2015"))

if __name__ == '__main__':
    main()
Free Stub

當Stub()不帶參數(shù)的時候诡曙,稱之為Free Stub。由于Free Stub沒有指定被替換的依賴服務(wù)种柑,所以Free Stub的屬性不受任何限制岗仑,可以自由定義。

from unittest import TestCase, main
from doublex_demo.Service import dataService as ds
from doublex_demo.Service import profileService as pos
from doublex_demo.Service import playerService as pls
from doublex_demo.Service import bodyService as bs
from doublex import Stub, ANY_ARG, assert_that, is_

class TestStub(TestCase):

    def test_stub(self):

        playername = "Kawhi Leonard"

        #當Stub()不帶參數(shù)的時候聚请,稱之為Free Stub
        #ANY_ARG表示任意參數(shù)
        with Stub() as freestub:
            freestub.get_assist().returns("6")
            freestub.get_score().returns("30")
            freestub.get_rebound().returns("8")
            freestub.get_match_number(ANY_ARG).returns(playername + " plays 82 games")

        player_service_stub_2017 = pls.playerService(playername, 2017, freestub, pos.profileService(playername), bs.bodyService(), ss.salaryService())
        #使用freestub代替dataService荠雕,來對待測對象playerService進行測試驗證
        assert_that(player_service_stub_2017.get_player_info().split('\n')[-2], is_("8"))
        assert_that(player_service_stub_2017.get_player_info().split('\n')[-1], is_("Kawhi Leonard plays 82 games"))

if __name__ == '__main__':
    main()

除了上述的returns()方法外,常用的還有raises()來模擬異常情況

def test_raises(self):
    with Stub() as stub:
        stub.foo(2).raises(Exception)

    with self.assertRaises(Exception):
        stub.foo(2)

#stub.foo()的調(diào)用會發(fā)生異常

如果沒有使用returns()方法驶赏,默認的返回值為None炸卑。

from doublex import Stub
# 如果不是定義返回值,不需要使用with關(guān)鍵字來定義stub
stub = Stub()
stub.foo() #這個方法會返回None

#定義返回值煤傍,需要使用下面的方式
with Stub() as stub1:
  stub1.foo(1).returns("1")
Ad-hoc Stub

通過方法method_returning()method_raising()盖文,我們可以實現(xiàn)對實例建立stub。這種方法不需要使用Stub()就可以建立stub了蚯姆。

def test_adhoc_stub(self):
       bodyservice = bs.bodyService()
       #method_returning()直接在實例上建立stub五续,并設(shè)定返回值
       bodyservice.get_height = method_returning("210cm")
       assert_that(bodyservice.get_height(), is_("210cm"))
       #method_raising()直接在實例上建立stub,并拋出異常
       bodyservice.get_weight = method_raising(Exception)
       with self.assertRaises(Exception):
           bodyservice.get_weight()

Test Spy - Spies remember everything that happens to them

doublex提供了Spy()對象來創(chuàng)建Test Spy龄恋,根據(jù)參數(shù)的不同分為 Spy疙驾,Free SpyProxy Spy

下面的例子中,我們使用Spy(Collaborator)來代替相應(yīng)的依賴服務(wù)郭毕,然后驗證SUT是否正確的調(diào)用
依賴服務(wù)和傳遞參數(shù)它碎。

Spy
from unittest import TestCase, main
from doublex_demo.Service import dataService as ds
from doublex_demo.Service import profileService as pos
from doublex_demo.Service import playerService as pls
from doublex import Spy, called, ProxySpy, assert_that
from doublex_demo.Service import bodyService as bs
from doublex_demo.Service import salaryService as ss

class TestSpy(TestCase):

    def test_spy(self):

        playername = "Kawhi Leonard"
        year = 2017
        salary = "20m"

        #使用Spy(類對象)來創(chuàng)建spy
        spy_ss = Spy(ss.salaryService)
        #通過SUT調(diào)用spy對象的方法
        pls.playerService(playername, 2017, ds.dataService(playername), pos.profileService(playername), bs.bodyService(), spy_ss).set_new_salary(salary)
        #驗證spy_ss.set_salary方法被調(diào)用過
        assert_that(spy_ss.set_salary, called())

        #Spy是Stub的擴展,所以除了記錄方法被調(diào)用的情況显押,也可以設(shè)定返回值
        with Spy(bs.bodyService) as spy_bs_as_stub:
            spy_bs_as_stub.get_height().returns("188cm")
            spy_bs_as_stub.get_weight().returns("110kg")
            spy_bs_as_stub.illnessHistory(2017).returns("Year 2017 no injury")
            spy_bs_as_stub.illnessHistory(2018).returns("Year 2017 has ankle injury")
        #直接調(diào)用spy對象方法
        spy_bs_as_stub.get_height()
        spy_bs_as_stub.get_weight()
        spy_bs_as_stub.illnessHistory(2017)
        spy_bs_as_stub.illnessHistory(2018)
        #可以驗證spy對象方法已經(jīng)被調(diào)用及其參數(shù)接受情況
        assert_that(spy_bs_as_stub.get_height, called())
        assert_that(spy_bs_as_stub.get_weight, called())
        assert_that(spy_bs_as_stub.illnessHistory, called().times(2))
        #使用anything()去任意匹配
        assert_that(spy_bs_as_stub.illnessHistory, called().with_args(anything()))        #通過SUT調(diào)用spy對象的方法
        player_service_spy_2016 = pls.playerService(playername, 2017, ds.dataService(playername), pos.profileService(playername), spy_bs_as_stub, ss.salaryService())
        player_service_spy_2016.get_physical_feature(2017)
        #驗證spy對象方法再一次被方法(SUT)調(diào)用
        assert_that(spy_bs_as_stub.get_height, called().times(2))
        assert_that(spy_bs_as_stub.get_weight, called().times(2))
        assert_that(spy_bs_as_stub.illnessHistory, called().times(3))

if __name__ == '__main__':
    main()
Free Spy
from unittest import TestCase, main
from doublex_demo.Service import dataService as ds
from doublex_demo.Service import profileService as pos
from doublex_demo.Service import playerService as pls
from doublex import Spy, called, ProxySpy, assert_that
from doublex_demo.Service import bodyService as bs
from doublex_demo.Service import salaryService as ss

class TestSpy(TestCase):

    def test_spy(self):

        playername = "Kawhi Leonard"
        year = 2017
        salary = "20m"
        #使用with關(guān)鍵字和Spy()來創(chuàng)建free spy
        #設(shè)置和salaryService一樣的方法
        with Spy() as free_ss_spy:
            free_ss_spy.set_salary(salary).returns("20m")
        #通過SUT調(diào)用spy對象的方法
        pls.playerService(playername, 2017, ds.dataService(playername), pos.profileService(playername), bs.bodyService(), free_ss_spy).set_new_salary(salary)
        #驗證spy_ss.set_salary方法被調(diào)用過
        assert_that(free_ss_spy.set_salary, called())

if __name__ == '__main__':
    main()
ProxySpy

ProxySpy()Spy()不同的地方是ProxySpy()接受的對象是實例扳肛。

不過我們要注意盡量不要使用ProxySpy(),官方文檔給出了如下解釋:

Note the ProxySpy breaks isolation. It is not really a double. Therefore is always the worst double and the last resource.

from unittest import TestCase, main
from doublex_demo.Service import dataService as ds
from doublex_demo.Service import profileService as pos
from doublex_demo.Service import playerService as pls
from doublex import Spy, called, ProxySpy, assert_that
from doublex_demo.Service import bodyService as bs
from doublex_demo.Service import salaryService as ss

class TestSpy(TestCase):

    def test_spy(self):

        playername = "Kawhi Leonard"
        year = 2017
        salary = "20m"

        #傳遞實例給ProxySpy()
        spy_pos = ProxySpy(pos.profileService(playername))
        #通過SUT調(diào)用spy對象的方法
        pls.playerService(playername, 2016, ds.dataService(playername), spy_pos, bs.bodyService(), ss.salaryService()).get_player_info()
        #驗證spy對象方法被調(diào)用過
        assert_that(spy_pos.get_player_team, called())

if __name__ == '__main__':
    main()

Spy的驗證最常用的是下面兩個方法

  1. called()驗證方法調(diào)用情況
  2. with_args()驗證參數(shù)調(diào)用情況

一個典型的例子如下

from hamcrest import contains_string, less_than, greater_than
from doublex import Spy, assert_that, called
#不設(shè)置返回值,可以不用with關(guān)鍵字
spy = Spy()

spy.m1()
spy.m2(None)
spy.m3(2)
spy.m4("hi", 3.0)
spy.m5([1, 2])
spy.m6(name="john doe")

assert_that(spy.m1, called())
assert_that(spy.m2, called())

assert_that(spy.m1, called().with_args())
assert_that(spy.m2, called().with_args(None))
assert_that(spy.m3, called().with_args(2))
assert_that(spy.m4, called().with_args("hi", 3.0))
assert_that(spy.m5, called().with_args([1, 2]))
assert_that(spy.m6, called().with_args(name="john doe"))
#使用hamcrest matchers豐富判斷條件
assert_that(spy.m3, called().with_args(less_than(3)))
assert_that(spy.m3, called().with_args(greater_than(1)))
assert_that(spy.m6, called().with_args(name=contains_string("doe")))

calls()方法可以幫助我們獲得更加具體的參數(shù)值和返回值乘碑,從而進行復(fù)雜的驗證

def test_calls_spy(self):
    salary = "20m"
    year = 2017
    #創(chuàng)建spy
    with Spy(ss.salaryService) as ss_spy:
        ss_spy.set_salary(salary)
    #調(diào)用方法
    ss_spy.set_salary(salary)
    ss_spy.set_salary("22m")
    #使用calls取得調(diào)用傳入的參數(shù)
    #多次調(diào)用可以多次取得挖息,calls是一個數(shù)組
    assert_that(ss_spy.set_salary.calls[0].args, is_((salary, )))
    assert_that(ss_spy.set_salary.calls[1].args, is_(("22m", )))

    #創(chuàng)建spy
    with Spy(bs.bodyService) as bs_spy:
        bs_spy.get_height().returns("190cm")
        bs_spy.illnessHistory(year).returns("no injury")
    #調(diào)用方法
    bs_spy.get_height()
    bs_spy.illnessHistory(year)
    #使用calls取得調(diào)用傳入的參數(shù)和返回值
    assert_that(bs_spy.get_height.calls[0].retval, is_("190cm"))
    assert_that(bs_spy.illnessHistory.calls[0].args, is_((year, )))
    assert_that(bs_spy.illnessHistory.calls[0].retval, is_("no injury"))

Mock Object - Mock forces the predefined script

doublex提供了Mock()來實現(xiàn)Mock Object的創(chuàng)建。Mock Object預(yù)先定義了一些方法調(diào)用的順序蝉仇,然后Mock Object被調(diào)用的時候旋讹,會去驗證方法是否按照預(yù)定義的順序和參數(shù)調(diào)用殖蚕。驗證很簡單地使用doublex.verify()方法,如果不去驗證調(diào)用的順序沉迹,可以使用doublex.any_order_verify()睦疫。

from unittest import TestCase, main
from doublex_demo.Service import dataService as ds
from doublex_demo.Service import profileService as pos
from doublex_demo.Service import playerService as pls
from doublex import Mock, verify, assert_that, any_order_verify
from doublex_demo.Service import bodyService as bs
from doublex_demo.Service import salaryService as ss

class TestSpy(TestCase):

    def test_spy(self):

        playername = "Kawhi Leonard"
        year = 2017
        salary = "20m"
        #使用with關(guān)鍵字和Mock()創(chuàng)建mock object
        #假設(shè)替代salaryService對象
        #定義mock需要調(diào)用的方法及其參數(shù),此方法與被替代的salaryService中的方法相同
        with Mock() as mock:
            mock.set_salary("20m")
        #在SUT playerservice中調(diào)用這個mock
        #之前定義的mock.set_salary("20m")會被SUT調(diào)用
        player_service_mock_2017 = pls.playerService(playername, year, ds.dataService(playername), pos.profileService(playername), bs.bodyService(), mock)
        player_service_mock_2017.set_new_salary(salary)
        #verify()驗證定義的mock期望是否正確被實現(xiàn)
        assert_that(mock, verify())

        #假設(shè)替代dataService對象
        #mock可以設(shè)置返回值
        with Mock() as mock_order:
            mock_order.get_score().returns("22")
            mock_order.get_assist().returns("3")
            mock_order.get_rebound().returns("6")
            mock_order.get_match_number(year).returns("77")
        #在SUT playerservice中調(diào)用這個mock
        player_service_mock_2017_order = pls.playerService(playername, year, mock_order, pos.profileService(playername), bs.bodyService(), ss.salaryService())
        player_service_mock_2017_order.get_player_info()
        # verify()驗證定義的mock期望是否正確被實現(xiàn)鞭呕,且方法調(diào)用順序必須完全一致
        assert_that(mock_order, verify())

        #假設(shè)替代dataService對象蛤育,注意mock定義中期望的順序和之前不一樣,也會和執(zhí)行順序不一致
        with Mock() as mock_any_order:
            mock_any_order.get_score().returns("22")
            mock_any_order.get_rebound().returns("6")
            mock_any_order.get_match_number(year).returns("77")
            mock_any_order.get_assist().returns("3")
        #在SUT playerservice中調(diào)用這個mock
        player_service_mock_2017_any_order = pls.playerService(playername, year, mock_any_order, pos.profileService(playername),
                                                         bs.bodyService(), ss.salaryService())
        player_service_mock_2017_any_order.get_player_info()
        #any_order_verify()驗證定義的mock期望是否正確被實現(xiàn)葫松,且方法調(diào)用順序不要求完全一致
        assert_that(mock_any_order, any_order_verify())

if __name__ == '__main__':
    main()

Stubbing properties

對于使用@property的類瓦糕,doublex提供了非常簡單的處理

#假設(shè)Student是我們需要替換的類
class Student(object):

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value

from doublex import Spy, Mock, Stub, assert_that, is_
from doublex import property_got, property_set, never, verify

#創(chuàng)建stub,可以直接復(fù)制腋么,相當于set操作
with Stub(Student) as stub:
    stub.score = 88
assert_that(stub.score, is_(88))

#建立spy
spy = Spy(Student)
#讀取score屬性咕娄,由于沒有設(shè)置返回值,這里的value會是None
value = spy.score
#使用property_got()方法驗證屬性是否被調(diào)用
assert_that(spy, property_got('score'))
spy.score = 59
spy.score = 98
spy.score = 98
#使用property_set()方法驗證賦值情況
assert_that(spy, property_set('score').to(59))
assert_that(spy, property_set('score').to(98).times(2))
assert_that(spy, never(property_set('score').to(99)))

#創(chuàng)建mock
with Mock(Student) as mock:
    #設(shè)定期望為調(diào)用score
    mock.score
#調(diào)用score
mock.score
#verify()驗證
assert_that(mock, verify())

#創(chuàng)建mock
with Mock(Student) as mock1:
    #設(shè)定期望為score被賦值為80
    mock1.score = 80
#賦值80
mock1.score=80
#verify()驗證
assert_that(mock1, verify())

Stub delegates

對于Stub返回值的設(shè)定珊擂,除了returns()方法圣勒,doublex還提供了一個delegates()方法。delegates()方法接受函數(shù)或生成器或其他可迭代的對象為參數(shù)摧扇。

def test_delegate_stub(self):
    def get_height():
        return "181cm"
    #創(chuàng)建stub
    with Stub(bs.bodyService) as stub:
        #使用delegates()來設(shè)定返回值圣贸,接受方法或是可以迭代的對象
        stub.get_height().delegates(get_height)
        stub.get_weight().delegates(["120kg", "121kg"])
    #驗證返回值
    assert_that(stub.get_height(), is_("181cm"))
    assert_that(stub.get_weight(), is_("120kg"))
    assert_that(stub.get_weight(), is_("121kg"))

Stub observer

Stub的方法是可以被觀察的】富可以使用attach()方法把一個任意方法和Stub方法綁定起來吁峻,然后在每次Stub方法調(diào)用的時候,這個attached的方法也會被調(diào)用在张。這樣的話用含,我們就可以在Stub中執(zhí)行其他代碼。

def test_observer_stub(self):
    def bar():
        print("I am attached")
    with Stub() as stub:
        stub.foo().returns("I am foo")
        stub.foo.attach(bar)
    #bar()會在這里執(zhí)行
    assert_that(stub.foo(), is_("I am foo"))

Inline stubbing and mocking

doublex創(chuàng)建mock/stub/spy一般使用的是double context manager的方式帮匾,語法如下所示

from doublex import Stub

with Stub() as stub:
     stub.method(<args>).returns(<value>)

為了易讀性耕餐,doublex還提供了when()expect_all()來實現(xiàn)同樣的創(chuàng)建功能。

when()用于stub和spy

def test_inline_stub(self):
    #Stub()創(chuàng)建free stub
    inline_stub_free = Stub()
    #使用when()設(shè)置方法參數(shù)和返回值
    when(inline_stub_free).foo(1).returns("I am inline free stub")
    assert_that(inline_stub_free.foo(1), is_("I am inline free stub"))
    #Stub(Collaborator)創(chuàng)建stub
    inline_stub = Stub(bs.bodyService)
    # 使用when()設(shè)置方法參數(shù)和返回值
    when(inline_stub).get_height().returns("188cm")
    assert_that(inline_stub.get_height(), is_("188cm"))
def test_inline_spy(self):
    #Spy()創(chuàng)建free spy
    spy_inline_free = Spy()
    #使用when()設(shè)置方法參數(shù)和返回值
    when(spy_inline_free).foo().returns("I am inline foo")
    #調(diào)用方法
    spy_inline_free.foo()
    #驗證調(diào)用情況
    assert_that(spy_inline_free.foo(), is_("I am inline foo"))
    assert_that(spy_inline_free.foo, called())

    #Spy()創(chuàng)建spy
    spy_inline = Spy(ss.salaryService)
    #使用when()設(shè)置方法參數(shù)
    when(spy_inline).set_salary(ANY_ARG)
    #調(diào)用方法
    spy_inline.set_salary("12m")
    #驗證調(diào)用情況
    assert_that(spy_inline.set_salary, called().with_args("12m"))

expect_all()用于mock

def test_inline_mock(self):
    playername = "Kawhi Leonard"
    year = 2017
    #使用Mock()創(chuàng)建mock
    inline_mock = Mock()
    #使用expect_all()去設(shè)置期望值
    expect_call(inline_mock).get_score().returns("33")
    expect_call(inline_mock).get_assist().returns("6")
    expect_call(inline_mock).get_rebound().returns("7")
    expect_call(inline_mock).get_match_number(year).returns("no injury")
    #在SUT playerservice中調(diào)用這個mock
    player_service_mock_2017_order = pls.playerService(playername, year, inline_mock, pos.profileService(playername), bs.bodyService(), ss.salaryService())
    player_service_mock_2017_order.get_player_info()
    # verify()驗證定義的mock期望是否正確被實現(xiàn)辟狈,且方法調(diào)用順序必須完全一致
    assert_that(inline_mock, verify())

Asynchronous spies

有些情況下SUT調(diào)用依賴組件是一個異步行為,有可能依賴組件的調(diào)用執(zhí)行是延后的夏跷,這樣的話就會產(chǎn)生下面的問題

# THE WRONG WAY
class AsyncTests(unittest.TestCase):
    def test_wrong_try_to_test_an_async_invocation(self):
        # given
        spy = Spy(Collaborator)
        sut = SUT(spy)

        # when
        sut.some_method()

        # then
        assert_that(spy.write, called())

上面代碼中哼转,called()的驗證是有可能在spy.write()執(zhí)行之前就進行了。

doublex提供了一個called.async(timeout) matcher來支持異步的spy驗證

The called assertion waits the corresponding invocation a maximum of timeout seconds.

# THE DOUBLEX WAY
class AsyncTests(unittest.TestCase):
    def test_test_an_async_invocation_with_doublex_async(self):
        # given
        spy = Spy(Collaborator)
        sut = SUT(spy)

        # when
        sut.some_method()

        # then
        assert_that(spy.write, called().async(timeout=1))

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末槽华,一起剝皮案震驚了整個濱河市壹蔓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌猫态,老刑警劉巖佣蓉,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件披摄,死亡現(xiàn)場離奇詭異,居然都是意外死亡勇凭,警方通過查閱死者的電腦和手機疚膊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來虾标,“玉大人寓盗,你說我怎么就攤上這事¤岛” “怎么了傀蚌?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蘸吓。 經(jīng)常有香客問我善炫,道長,這世上最難降的妖魔是什么库继? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任箩艺,我火速辦了婚禮,結(jié)果婚禮上制跟,老公的妹妹穿的比我還像新娘舅桩。我一直安慰自己,他們只是感情好雨膨,可當我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布擂涛。 她就那樣靜靜地躺著,像睡著了一般聊记。 火紅的嫁衣襯著肌膚如雪撒妈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天排监,我揣著相機與錄音狰右,去河邊找鬼。 笑死舆床,一個胖子當著我的面吹牛棋蚌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播挨队,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼谷暮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了盛垦?” 一聲冷哼從身側(cè)響起湿弦,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎腾夯,沒想到半個月后颊埃,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蔬充,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年班利,在試婚紗的時候發(fā)現(xiàn)自己被綠了饥漫。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡肥败,死狀恐怖趾浅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情馒稍,我是刑警寧澤皿哨,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站纽谒,受9級特大地震影響证膨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鼓黔,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一央勒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧澳化,春花似錦崔步、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至列林,卻和暖如春瑞你,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背希痴。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工者甲, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人砌创。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓虏缸,卻偏偏與公主長得像,于是被迫代替她去往敵國和親嫩实。 傳聞我的和親對象是個殘疾皇子寇钉,可洞房花燭夜當晚...
    茶點故事閱讀 45,515評論 2 359

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)舶赔,斷路器,智...
    卡卡羅2017閱讀 134,702評論 18 139
  • 1.Creating mock objects 1.1Class mocks idclassMock=OCMCla...
    奔跑的小小魚閱讀 2,595評論 0 0
  • 單元測試實踐背景 測試環(huán)境定位bug時谦秧,需要測試同學(xué)協(xié)助手動發(fā)起相關(guān)業(yè)務(wù)URL請求,開發(fā)進行遠程調(diào)試問題:1、遠程...
    Zeng_小洲閱讀 7,688評論 0 4
  • Test Double 第一次了解Test Double是在Martin Fowler的文章Test Double...
    做測試的DanteYu閱讀 8,489評論 2 8
  • 秋行级乍,仿早發(fā)白帝城 朝辭長安水云間淤齐, 千里東海一日還。 滿目秋田晴忽雨蒜撮, 輕車已過大江南。 寒露月 萬頃金風(fēng)盈銀漢...
    南渭川閱讀 224評論 0 1