双重for_测试双重图案
雙重for
前段時(shí)間,我寫了一篇有關(guān)使用Test Double的后果的文章,但是與Test Double Patterns無關(guān),僅是一個(gè)簡單的清單。 今天,我想對其進(jìn)行更改,并解釋這些模式之間的差異。
正如我在提到的文章中寫道:
Test Double是允許我們控制被測單元之間依賴性的模式。 為了能夠在我們想要或/和/或驗(yàn)證是否發(fā)生想要的行為時(shí)提供想要的行為。
因此,現(xiàn)在當(dāng)您想起了基礎(chǔ)知識時(shí),我們可以轉(zhuǎn)到有趣的部分–讓我們看一下“測試雙重模式”。
虛擬對象
虛擬是TD(測試雙精度),當(dāng)我們想要傳遞對象以填充參數(shù)列表時(shí)使用。 從未實(shí)際使用過。 這就是為什么它不總是被視為TD之一的原因-它不提供任何行為。
假設(shè)我們有發(fā)送報(bào)告的Sender類。 由于某些要求,我們需要將其包裝到另一個(gè)類中以提供有效的接口。 我們的課看起來像這樣:
public class ReportProcessor implements Processor {private Sender sender;public ReportProcessor(Sender sender) {this.sender = sender;}@Overridepublic void process(Report report) {sender.send(report);} }現(xiàn)在,我們的測試是什么樣的? 我們需要驗(yàn)證什么? 我們必須檢查報(bào)告是否傳遞給Sender實(shí)例的send()方法。 可以按照以下步驟完成:
public class DummyTest {@Testpublic void shouldSentReportWhileProcessing() {Sender sender = aMessageSender();ReportProcessor reportProcessor = aReportProcessor(sender);Report dummyReport = new Report();reportProcessor.process(dummyReport);then(sender).should().send(dummyReport);}private ReportProcessor aReportProcessor(Sender sender) {return new ReportProcessor(sender);}private Sender aMessageSender() {return spy(Sender.class);} }如您所見,沒有與我們的虛擬對象進(jìn)行交互。 僅創(chuàng)建報(bào)告并將其作為參數(shù)傳遞。 沒有行為,只有存在。
假物件
Fake Object只是測試類所依賴的對象的一種更簡單,更輕量的實(shí)現(xiàn)。 它提供了預(yù)期的功能。
在決定時(shí)要記住的重要事項(xiàng)是使其盡可能簡單。 任何其他邏輯可能會對測試的脆弱性和準(zhǔn)確性產(chǎn)生重大影響。
假設(shè)我們有一個(gè)帶有create()方法的ReportService,它的職責(zé)是僅在尚未創(chuàng)建Report的情況下創(chuàng)建一個(gè)Report。 為簡單起見,我們可以假定標(biāo)題標(biāo)識一個(gè)對象–我們不能有兩個(gè)標(biāo)題相同的報(bào)表:
public class ReportService {private ReportRepository reportRepository;public ReportService(ReportRepository reportRepository) {this.reportRepository = reportRepository;}public void create(Title title, Content content) {if (!reportRepository.existsWithTitle(title)) {Report report = new Report(title, content);reportRepository.add(report);}} }我們可以通過多種方式測試此代碼,但是我們將決定使用Fake Object:
class FakeReportRepository implements ReportRepository {private Map<Title, Report> reports = new HashMap<>();@Overridepublic void add(Report report) {reports.put(report.title(), report);}@Overridepublic boolean existsWithTitle(Title title) {return reports.containsKey(title);}@Overridepublic int countAll() {return reports.size();}@Overridepublic Report findByTitle(Title title) {return reports.get(title);} }我們的測試將如下所示:
public class FakeTest {@Testpublic void shouldNotCreateTheSameReportTwice() {FakeReportRepository reportRepository = new FakeReportRepository();ReportService reportService = aReportService(reportRepository);reportService.create(DUMMY_TITLE, DUMMY_CONTENT);reportService.create(DUMMY_TITLE, DUMMY_CONTENT);Report createdReport = reportRepository.findByTitle(DUMMY_TITLE);assertThat(createdReport.title()).isSameAs(DUMMY_TITLE);assertThat(createdReport.content()).isSameAs(DUMMY_CONTENT);assertThat(reportRepository.countAll()).isEqualTo(1);}private ReportService aReportService(ReportRepository reportRepository) {return new ReportService(reportRepository);} }存根對象
我們在對方法輸出感興趣的情況下使用Stub Object,以確保每次調(diào)用它時(shí)結(jié)果都將完全符合我們的期望。
通常,我們不會在測試中檢查是否調(diào)用了Stub,因?yàn)槲覀儠ㄟ^其他斷言知道它。
在此示例中,我們將查看一個(gè)ReportFactory,該工廠將創(chuàng)建具有創(chuàng)建日期的報(bào)表。 為了可測試性,我們使用了依賴注入來注入DateProvider:
public class ReportFactory {private DateProvider dateProvider;public ReportFactory(DateProvider dateProvider) {this.dateProvider = dateProvider;}public Report crete(Title title, Content content) {return new Report(title, content, dateProvider.date());} }它允許我們在測試中使用Stub:
public class StubTest {@Testpublic void shouldCreateReportWithCreationDate() {Date dummyTodayDate = new Date();DateProvider dateProvider = mock(DateProvider.class);stub(dateProvider.date()).toReturn(dummyTodayDate);ReportFactory reportFactory = new ReportFactory(dateProvider);Report report = reportFactory.crete(DUMMY_TITLE, DUMMY_CONTENT);assertThat(report.creationDate()).isSameAs(dummyTodayDate);} }如您所見,我們僅對調(diào)用Stub的結(jié)果感興趣。
間諜對象
與Stub對象相反,當(dāng)我們對間諜方法的輸入感興趣時(shí),我們將使用Spies。 我們正在檢查它是否被調(diào)用。 我們可以檢查它被調(diào)用了多少次。
我們也可以將實(shí)際的應(yīng)用程序?qū)ο笥米鱏pies。 無需創(chuàng)建任何其他類。
讓我們從第一段回到ReportProcessor:
public class ReportProcessor implements Processor {// code@Overridepublic void process(Report report) {sender.send(report);} }可能您已經(jīng)注意到我們在那里使用了Spy,但讓我們再次看一下測試:
public class SpyTest {@Testpublic void shouldSentReportWhileProcessing() {Sender sender = spy(Sender.class);ReportProcessor reportProcessor = aReportProcessor(sender);reportProcessor.process(DUMMY_REPORT);then(sender).should().send(DUMMY_REPORT);}private ReportProcessor aReportProcessor(Sender sender) {return new ReportProcessor(sender);} }我們要檢查對象是否以正確的方式包裝,并將參數(shù)傳遞給其(包裝的對象)方法調(diào)用。 這就是為什么我們在這里使用Spy。
模擬對象
模擬對象通常被描述為Stub和Spy的組合。 我們指定期望接收的輸入,并在此基礎(chǔ)上返回正確的結(jié)果。
如果這是我們期望的,則調(diào)用模擬對象也可能導(dǎo)致拋出異常。
好的,讓我們再次看一下ReportService:
public class ReportService {//codepublic void create(Title title, Content content) {if (!reportRepository.existsWithTitle(title)) {Report report = new Report(title, content);reportRepository.add(report);}} }現(xiàn)在,我們將使用模擬對象代替?zhèn)螌ο?#xff1a;
@RunWith(MockitoJUnitRunner.class) public class MockTest {@Mock private ReportRepository reportRepository;@InjectMocks private ReportService reportService;@Testpublic void shouldCreateReportIfDoesNotExist() {given(reportRepository.existsWithTitle(DUMMY_TITLE)).willReturn(false);reportService.create(DUMMY_TITLE, DUMMY_CONTENT);then(reportRepository).should().add(anyReport());}@Testpublic void shouldNotCreateReportIfDoesNotExist() {given(reportRepository.existsWithTitle(DUMMY_TITLE)).willReturn(true);reportService.create(DUMMY_TITLE, DUMMY_CONTENT);then(reportRepository).should(never()).add(anyReport());}private Report anyReport() {return any(Report.class);} }為了澄清一切,我們的模擬對象是ReportRepository.existsWithTitle()方法。 如您所見,在第一個(gè)測試中,我們說如果調(diào)用帶有DUMMY_OBJECT參數(shù)的方法,它將返回true。 在第二個(gè)測試中,我們檢查相反的情況。
我們在兩個(gè)測試中的最后一個(gè)斷言(then()。should())是另一個(gè)TD模式。 你能認(rèn)出哪一個(gè)嗎?
最后一句話
這就是我今天要說的有關(guān)測試雙模式的全部內(nèi)容。 我鼓勵(lì)您有意使用它們,不要盲目遵循在可能的情況下添加@Mock注釋的習(xí)慣。
我還邀請您閱讀有關(guān)使用Test Double的后果的文章,以了解使用TD模式時(shí)可能遇到的問題以及如何識別和解決此類問題。
如果您想進(jìn)一步加深對這些模式的了解,那么會有一個(gè)很棒的頁面可以幫助您做到這一點(diǎn): xUnit Patterns:Test Double 。
祝您測試順利! 使它們可讀且有價(jià)值。
如果您對“測試雙模式”有任何想法或疑問,請?jiān)谠u論中分享。
翻譯自: https://www.javacodegeeks.com/2015/09/test-double-patterns.html
雙重for
總結(jié)
以上是生活随笔為你收集整理的双重for_测试双重图案的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 检测到基于堆栈的缓冲区溢出_检测到堆栈粉
- 下一篇: 春春幼儿园堆积木大赛_春云边车