摘要:例子使用源代碼我們先給了一個(gè)的實(shí)現(xiàn)然后又規(guī)定了方法的返回值。源代碼也就是說,得益于,我們能夠很方便地對依賴關(guān)系中任意層級的任意做。
Github地址
Mock測試技術(shù)能夠避免你為了測試一個(gè)方法,卻需要自行構(gòu)建整個(gè)依賴關(guān)系的工作,并且能夠讓你專注于當(dāng)前被測試對象的邏輯,而不是其依賴的其他對象的邏輯。
舉例來說,比如你需要測試Foo.methodA,而這個(gè)方法依賴了Bar.methodB,又傳遞依賴到了Zoo.methodC,于是它們的依賴關(guān)系就是Foo->Bar->Zoo,所以在測試代碼里你必須自行new Bar和Zoo。
有人會(huì)說:"我直接用Spring的DI機(jī)制不就行了嗎?"的確,你可以用Spring的DI機(jī)制,不過解決不了測試代碼耦合度過高的問題:
因?yàn)镕oo方法內(nèi)部調(diào)用了Bar和Zoo的方法,所以你對其做單元測試的時(shí)候,必須完全了解Bar和Zoo方法的內(nèi)部邏輯,并且謹(jǐn)慎的傳參和assert結(jié)果,一旦Bar和Zoo的代碼修改了,你的Foo測試代碼很可能就會(huì)運(yùn)行失敗。
所以這個(gè)時(shí)候我們需要一種機(jī)制,能過讓我們在測試Foo的時(shí)候不依賴于Bar和Zoo的具體實(shí)現(xiàn),即不關(guān)心其內(nèi)部邏輯,只關(guān)注Foo內(nèi)部的邏輯,從而將Foo的每個(gè)邏輯分支都測試到。
所以業(yè)界就產(chǎn)生了Mock技術(shù),它可以讓我們做一個(gè)假的Bar(不需要Zoo,因?yàn)橹挥姓娴腂ar才需要Zoo),然后控制這個(gè)假的Bar的行為(讓它返回什么就返回什么),以此來測試Foo的每個(gè)邏輯分支。
你肯定會(huì)問,這樣的測試有意義嗎?在真實(shí)環(huán)境里Foo用的是真的Bar而不是假的Bar,你用假的Bar測試成功能代表真實(shí)環(huán)境不出問題?
其實(shí)假Bar代表的是一個(gè)行為正確的Bar,用它來測試就能驗(yàn)證"在Bar行為正確的情況下Foo的行為是否正確",而真Bar的行為是否正確會(huì)由它自己的測試代碼來驗(yàn)證。
Mock技術(shù)的另一個(gè)好處是能夠讓你盡量避免集成測試,比如我們可以Mock一個(gè)Repository(數(shù)據(jù)庫操作類),讓我們盡量多寫單元測試,提高測試代碼執(zhí)行效率。
spring-boot-starter-test依賴了Mockito,所以我們會(huì)在本章里使用Mockito來講解。
被測試類先介紹一下接下來要被我們測試的類Foo、Bar倆兄弟。
public interface Foo { boolean checkCodeDuplicate(String code); } public interface Bar { Set例子1: 不使用Mock技術(shù)getAllCodes(); } @Component public class FooImpl implements Foo { private Bar bar; @Override public boolean checkCodeDuplicate(String code) { return bar.getAllCodes().contains(code); } @Autowired public void setBar(Bar bar) { this.bar = bar; } }
源代碼NoMockTest:
public class NoMockTest { @Test public void testCheckCodeDuplicate1() throws Exception { FooImpl foo = new FooImpl(); foo.setBar(new Bar() { @Override public SetgetAllCodes() { return Collections.singleton("123"); } }); assertEquals(foo.checkCodeDuplicate("123"), true); } @Test public void testCheckCodeDuplicate2() throws Exception { FooImpl foo = new FooImpl(); foo.setBar(new FakeBar(Collections.singleton("123"))); assertEquals(foo.checkCodeDuplicate("123"), true); } public class FakeBar implements Bar { private final Set codes; public FakeBar(Set codes) { this.codes = codes; } @Override public Set getAllCodes() { return codes; } } }
這個(gè)測試代碼里用到了兩種方法來做假的Bar:
匿名內(nèi)部類
做了一個(gè)FakeBar
這兩種方式都不是很優(yōu)雅,看下面使用Mockito的例子。
例子2:使用Mockito源代碼MockitoTest:
public class MockitoTest { @Mock private Bar bar; @InjectMocks private FooImpl foo; @BeforeMethod(alwaysRun = true) public void initMock() { MockitoAnnotations.initMocks(this); } @Test public void testCheckCodeDuplicate() throws Exception { when(bar.getAllCodes()).thenReturn(Collections.singleton("123")); assertEquals(foo.checkCodeDuplicate("123"), true); } }
我們先給了一個(gè)Bar的Mock實(shí)現(xiàn):@Mock private Bar bar;
然后又規(guī)定了getAllCodes方法的返回值:when(bar.getAllCodes()).thenReturn(Collections.singleton("123"))。這樣就把一個(gè)假的Bar定義好了。
最后利用Mockito把Bar注入到Foo里面,@InjectMocks private FooImpl foo;、MockitoAnnotations.initMocks(this);
例子3:配合Spring Test源代碼Spring_1_Test:
@ContextConfiguration(classes = FooImpl.class) @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) public class Spring_1_Test extends AbstractTestNGSpringContextTests { @MockBean private Bar bar; @Autowired private Foo foo; @Test public void testCheckCodeDuplicate() throws Exception { when(bar.getAllCodes()).thenReturn(Collections.singleton("123")); assertEquals(foo.checkCodeDuplicate("123"), true); } }
要注意,如果要啟用Spring和Mockito,必須添加這么一行:@TestExecutionListeners(listeners = MockitoTestExecutionListener.class)。
例子4:配合Spring Test(多層依賴)當(dāng)Bean存在這種依賴關(guān)系當(dāng)時(shí)候:LooImpl -> FooImpl -> Bar,我們應(yīng)該怎么測試呢?
按照Mock測試的原則,這個(gè)時(shí)候我們應(yīng)該mock一個(gè)Foo對象,把這個(gè)注入到LooImpl對象里,就像例子3里的一樣。
不過如果你不想mock Foo而是想mock Bar的時(shí)候,其實(shí)做法和前面也差不多,Spring會(huì)自動(dòng)將mock Bar注入到FooImpl中,然后將FooImpl注入到LooImpl中。
源代碼Spring_2_Test:
@ContextConfiguration(classes = { FooImpl.class, LooImpl.class }) @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) public class Spring_2_Test extends AbstractTestNGSpringContextTests { @MockBean private Bar bar; @Autowired private Loo loo; @Test public void testCheckCodeDuplicate() throws Exception { when(bar.getAllCodes()).thenReturn(Collections.singleton("123")); assertEquals(loo.checkCodeDuplicate("123"), true); } }
也就是說,得益于Spring Test Framework,我們能夠很方便地對依賴關(guān)系中任意層級的任意Bean做mock。
例子5:配合Spring Boot Test源代碼Boot_1_Test:
@SpringBoot_1_Test(classes = { FooImpl.class }) @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) public class Boot_1_Test extends AbstractTestNGSpringContextTests { @MockBean private Bar bar; @Autowired private Foo foo; @Test public void testCheckCodeDuplicate() throws Exception { when(bar.getAllCodes()).thenReturn(Collections.singleton("123")); assertEquals(foo.checkCodeDuplicate("123"), true); } }參考文檔
Spring Framework Testing
Spring Boot Testing
Mockito
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/67442.html
摘要:首先先來看我們事先定義的以及??梢钥吹綍?huì)修改方法的返回值,使其返回。例子測試的行為最簡單的測試方法就是直接調(diào)用,看看它是否使用返回。先看這段代碼這些是利用提供的和來判斷是否被代理了的實(shí)現(xiàn)是通過動(dòng)態(tài)代理來做的。 Github地址 Spring提供了一套AOP工具,但是當(dāng)你把各種Aspect寫完之后,如何確定這些Aspect都正確的應(yīng)用到目標(biāo)Bean上了呢?本章將舉例說明如何對Spring...
摘要:地址提供了,能夠很方便的來測試。同時(shí)也提供了更進(jìn)一步簡化了測試需要的配置工作。本章節(jié)將分別舉例說明在不使用和使用下如何對進(jìn)行測試。例子測試的關(guān)鍵是使用對象,利用它我們能夠在不需啟動(dòng)容器的情況下測試的行為。 Github地址 Spring Testing Framework提供了Spring MVC Test Framework,能夠很方便的來測試Controller。同時(shí)Spring...
摘要:單元測試因?yàn)楣締卧獪y試覆蓋率需要達(dá)到,所以進(jìn)行單元測試用例編寫。測試的時(shí)候可以把每個(gè)判斷分支都走到。同這句代碼,可以通過如此一個(gè)對象,使用以上方法基本上可以編寫所有代碼的測試類。編寫測試一定程度上可以發(fā)現(xiàn)代碼錯(cuò)誤,可以借此重構(gòu)代碼。 3.springboot單元測試因?yàn)楣締卧獪y試覆蓋率需要達(dá)到80%,所以進(jìn)行單元測試用例編寫。多模塊項(xiàng)目的因?yàn)闀?huì)經(jīng)常調(diào)用其他服務(wù),而且避免數(shù)據(jù)庫操作對...
摘要:因?yàn)橹挥羞@樣才能夠在測試環(huán)境下發(fā)現(xiàn)生產(chǎn)環(huán)境的問題,也避免出現(xiàn)一些因?yàn)榕渲貌煌瑢?dǎo)致的奇怪問題。而方法則能夠不改變原有配置不提供新的配置的情況下,就能夠關(guān)閉。 Github地址 在Chapter 1: 基本用法 - 使用Spring Boot Testing工具里提到: 除了單元測試(不需要初始化ApplicationContext的測試)外,盡量將測試配置和生產(chǎn)配置保持一致。比如如果生產(chǎn)...
摘要:地址是提供的方便測試序列化反序列化的測試工具,在的文檔中有一些介紹。例子簡單例子源代碼見使用通包下的文件測試結(jié)果是否正確或者使用基于的校驗(yàn)例子測試可以用來測試。這個(gè)例子里使用了自定義的測試代碼例子使用事實(shí)上也可以配合一起使用。 Github地址 @JsonTest是Spring Boot提供的方便測試JSON序列化反序列化的測試工具,在Spring Boot的文檔中有一些介紹。 需要注...
閱讀 3425·2021-09-22 16:00
閱讀 3468·2021-09-07 10:26
閱讀 3029·2019-08-30 15:55
閱讀 2869·2019-08-30 13:48
閱讀 1376·2019-08-30 12:58
閱讀 2178·2019-08-30 11:15
閱讀 958·2019-08-30 11:08
閱讀 534·2019-08-29 18:41