摘要:例子使用源代碼我們先給了一個的實現然后又規定了方法的返回值。源代碼也就是說,得益于,我們能夠很方便地對依賴關系中任意層級的任意做。
Github地址
Mock測試技術能夠避免你為了測試一個方法,卻需要自行構建整個依賴關系的工作,并且能夠讓你專注于當前被測試對象的邏輯,而不是其依賴的其他對象的邏輯。
舉例來說,比如你需要測試Foo.methodA,而這個方法依賴了Bar.methodB,又傳遞依賴到了Zoo.methodC,于是它們的依賴關系就是Foo->Bar->Zoo,所以在測試代碼里你必須自行new Bar和Zoo。
有人會說:"我直接用Spring的DI機制不就行了嗎?"的確,你可以用Spring的DI機制,不過解決不了測試代碼耦合度過高的問題:
因為Foo方法內部調用了Bar和Zoo的方法,所以你對其做單元測試的時候,必須完全了解Bar和Zoo方法的內部邏輯,并且謹慎的傳參和assert結果,一旦Bar和Zoo的代碼修改了,你的Foo測試代碼很可能就會運行失敗。
所以這個時候我們需要一種機制,能過讓我們在測試Foo的時候不依賴于Bar和Zoo的具體實現,即不關心其內部邏輯,只關注Foo內部的邏輯,從而將Foo的每個邏輯分支都測試到。
所以業界就產生了Mock技術,它可以讓我們做一個假的Bar(不需要Zoo,因為只有真的Bar才需要Zoo),然后控制這個假的Bar的行為(讓它返回什么就返回什么),以此來測試Foo的每個邏輯分支。
你肯定會問,這樣的測試有意義嗎?在真實環境里Foo用的是真的Bar而不是假的Bar,你用假的Bar測試成功能代表真實環境不出問題?
其實假Bar代表的是一個行為正確的Bar,用它來測試就能驗證"在Bar行為正確的情況下Foo的行為是否正確",而真Bar的行為是否正確會由它自己的測試代碼來驗證。
Mock技術的另一個好處是能夠讓你盡量避免集成測試,比如我們可以Mock一個Repository(數據庫操作類),讓我們盡量多寫單元測試,提高測試代碼執行效率。
spring-boot-starter-test依賴了Mockito,所以我們會在本章里使用Mockito來講解。
被測試類先介紹一下接下來要被我們測試的類Foo、Bar倆兄弟。
public interface Foo { boolean checkCodeDuplicate(String code); } public interface Bar { Set例子1: 不使用Mock技術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; } } }
這個測試代碼里用到了兩種方法來做假的Bar:
匿名內部類
做了一個FakeBar
這兩種方式都不是很優雅,看下面使用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); } }
我們先給了一個Bar的Mock實現:@Mock private Bar bar;
然后又規定了getAllCodes方法的返回值:when(bar.getAllCodes()).thenReturn(Collections.singleton("123"))。這樣就把一個假的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(多層依賴)當Bean存在這種依賴關系當時候:LooImpl -> FooImpl -> Bar,我們應該怎么測試呢?
按照Mock測試的原則,這個時候我們應該mock一個Foo對象,把這個注入到LooImpl對象里,就像例子3里的一樣。
不過如果你不想mock Foo而是想mock Bar的時候,其實做法和前面也差不多,Spring會自動將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,我們能夠很方便地對依賴關系中任意層級的任意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
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/67442.html
摘要:首先先來看我們事先定義的以及。可以看到會修改方法的返回值,使其返回。例子測試的行為最簡單的測試方法就是直接調用,看看它是否使用返回。先看這段代碼這些是利用提供的和來判斷是否被代理了的實現是通過動態代理來做的。 Github地址 Spring提供了一套AOP工具,但是當你把各種Aspect寫完之后,如何確定這些Aspect都正確的應用到目標Bean上了呢?本章將舉例說明如何對Spring...
摘要:地址提供了,能夠很方便的來測試。同時也提供了更進一步簡化了測試需要的配置工作。本章節將分別舉例說明在不使用和使用下如何對進行測試。例子測試的關鍵是使用對象,利用它我們能夠在不需啟動容器的情況下測試的行為。 Github地址 Spring Testing Framework提供了Spring MVC Test Framework,能夠很方便的來測試Controller。同時Spring...
摘要:單元測試因為公司單元測試覆蓋率需要達到,所以進行單元測試用例編寫。測試的時候可以把每個判斷分支都走到。同這句代碼,可以通過如此一個對象,使用以上方法基本上可以編寫所有代碼的測試類。編寫測試一定程度上可以發現代碼錯誤,可以借此重構代碼。 3.springboot單元測試因為公司單元測試覆蓋率需要達到80%,所以進行單元測試用例編寫。多模塊項目的因為會經常調用其他服務,而且避免數據庫操作對...
摘要:因為只有這樣才能夠在測試環境下發現生產環境的問題,也避免出現一些因為配置不同導致的奇怪問題。而方法則能夠不改變原有配置不提供新的配置的情況下,就能夠關閉。 Github地址 在Chapter 1: 基本用法 - 使用Spring Boot Testing工具里提到: 除了單元測試(不需要初始化ApplicationContext的測試)外,盡量將測試配置和生產配置保持一致。比如如果生產...
摘要:地址是提供的方便測試序列化反序列化的測試工具,在的文檔中有一些介紹。例子簡單例子源代碼見使用通包下的文件測試結果是否正確或者使用基于的校驗例子測試可以用來測試。這個例子里使用了自定義的測試代碼例子使用事實上也可以配合一起使用。 Github地址 @JsonTest是Spring Boot提供的方便測試JSON序列化反序列化的測試工具,在Spring Boot的文檔中有一些介紹。 需要注...
閱讀 3420·2021-09-22 16:00
閱讀 3462·2021-09-07 10:26
閱讀 3019·2019-08-30 15:55
閱讀 2866·2019-08-30 13:48
閱讀 1376·2019-08-30 12:58
閱讀 2175·2019-08-30 11:15
閱讀 956·2019-08-30 11:08
閱讀 532·2019-08-29 18:41