乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      Spring項目單元測試

       昵稱27831725 2018-01-12

      Unit test與developing之間的矛盾由來已久,unit test帶來的時間成本是否能超過其對質(zhì)量的提升,每個團隊的結(jié)果都不相同。比如團結(jié)成熟度很高,那么一些簡單的unit test或許帶來不了什么收益;但是如果團隊比較年輕,成員也有很多經(jīng)驗不夠豐富的開發(fā)人員,不可避免會有一些低級bug出現(xiàn),unit test的收益就會相對明顯。做不做都是這個團隊的取舍。

      本文針對Spring項目的unit test提出幾種方案,并加以分析。Spring project的核心是bean,所以unit test不可避免需要能夠生產(chǎn)“bean”,因此有兩種實現(xiàn)方式:

      1. 加載spring配置,類似項目容器加載
      2. mock spring bean,對bean的調(diào)用方法進行攔截

      依賴spring bean的unit test測試方案

      這種方案的還原度最高,與真實運行的差別僅僅是容器,服務(wù)器環(huán)境等因素。常見的實現(xiàn)方案通過Spring Unit實現(xiàn),常見實現(xiàn)代碼如下。
      1. @RunWith(SpringJUnit4ClassRunner.class)  
      2. @ContextConfiguration(locations = {"classpath:spring-test-config.xml","xxx.xml"})  
      3. @TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)  
      4. @TestExecutionListeners( { xxxListener.class,xxxListener.class })  
      5. public class BaseTest extends AbstractTransactionalJUnit4SpringContextTests  
      6. public class BaseTest{  
      7.     //... 公用代碼部分  
      8. }  
      spring配置文件通過@ContextConfiguration注入,事務(wù)通過@TransactionConfiguration聲明。如果還有一些listener,可以通過@TestExecutionListeners方式注入?;旧峡梢詽M足測試需求。
      簡單的action示例,service同理。
      1. public class xxxTest extends BaseTest{  
      2.       
      3.     @Autowired  
      4.     private xxxBean xxxbean;  
      5.   
      6.     @Test  
      7.     public void tesr()  {  
      8.         MockHttpServletRequest request = new MockHttpServletRequest();  
      9.         request.setMethod("POST");  
      10.         request.addParameter(xxx,xxx);  
      11.         request.setServletPath(xxx);  
      12.         xxxbean.test(request);  
      13.         //....  
      14.         //multipart request  
      15.         //MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();  
      16.         //request.addFile(new MockMultipartFile("xxx.png","xxx.png",null, new FileInputStream("xxx.png")));  
      17.     }  
      18. }  


      如果涉及作用域問題,spring mock也提供支持。
      1. public class xxxTest extends BaseTest{  
      2.     @Autowired  
      3.     private  xxxController xxxController;  
      4.       
      5.     public ClassA test() {  
      6.         RequestContextListener listener = new RequestContextListener();  
      7.         MockServletContext context = new MockServletContext();  
      8.         MockHttpServletRequest request = new MockHttpServletRequest();  
      9.         MockHttpServletResponse response = new MockHttpServletResponse();  
      10.         request.setMethod("POST");  
      11.         request.addParameter("xxx", "xxx");  
      12.         request.setServletPath("xxx");  
      13.         listener.requestInitialized(new ServletRequestEvent(context, request));  
      14.         ClassA classa = xxxController.getClassA(request, response);  
      15.         Assert.assertNotNull(classa);  
      16.         return classa  
      17.     }  
      18. }  


      上面的示例中,需要注意的是,所有mock的對象的屬性,都要通過手動set。比如request的servletpath,multipart file的 originName。

      這種方案還原度很高,但是也帶來了弊端,比如datasource。spring源生的datasource是不支持多數(shù)據(jù)庫的,需要切換或者代碼端控制。而且從unit test的角度分析,測試邏輯不應(yīng)該依賴于datasource(根據(jù)unit test的專一性,datasource應(yīng)該有自己的unit test)。
      查閱資料發(fā)現(xiàn)有一種方案是采用h2代替真實的datasource,這樣整個測試過程的數(shù)據(jù)都在內(nèi)存里面,并不依賴真實db。筆者未實踐這種方案。

      第一種方案的核心思想是還原程序的運行環(huán)境,從真實測試過來來看,每個unit test都需要加載spring環(huán)境,帶來的結(jié)果是unit test運行時間過長。如果依賴datasource,某些dirty data有可能會影響測試結(jié)果。在這方面,mock test的方式執(zhí)行上更快。mock的框架很多,比如jmock,easymock,mockito等。這里筆者采用的是mockito + powermockito。

      Mock的思想比較接近unit test,不關(guān)心method的依賴。比如我有一個MethodA,其實現(xiàn)依賴于接口B和C,其中C又依賴接口D。在第一種方案中,該unit test需要執(zhí)行完B、C和D才能完成測試,但是其實B、C和D應(yīng)該都有自己的unit test,而且A并不關(guān)心依賴接口的實現(xiàn)。這里會出現(xiàn)大量的重復(fù)測試,并且如果B、C和D中任意一個接口存在缺陷,會導(dǎo)致A測試無法通過。


      采用Mock 后的結(jié)構(gòu)如下。A不在關(guān)心B和C的實現(xiàn),A只需要根據(jù)需求mockB和C的返回結(jié)果即可。理論上,只要B和C的返回正確,A的邏輯就算正確。至于B和C自身是否有問題,應(yīng)該交由B和C的unit test測試。這樣才能體現(xiàn)職責(zé)單一。



      Mockito的資料網(wǎng)上有很多,原理分析google和百度都有。其核心是stud和proxy。通過某種手段(尚未分析源碼)記錄mock的方法,通過proxy攔截其真實執(zhí)行,返回一個預(yù)先設(shè)置的值,從而達到mock的效果。
      做個簡單的demo。我現(xiàn)在有一個打印機(Interface Printer),想要打印兩串字符,一串數(shù)字和一串字母。
      1. public class Main {  
      2.     public static void main(String[] args) {  
      3.         Printer printer = new HpPrinter();  
      4.         String result1 = printer.print("abc");  
      5.         String result2 = pringter.print("1234");  
      6.         System.out.println("result1:" + result1);  
      7.         System.out.println("result2:" + result2);  
      8.     }  
      9. }  
      10.   
      11. public interface Printer {  
      12.       
      13.     public String print(String message);  
      14. }  
      15.   
      16. public class HpPrinter implements Printer{  
      17.   
      18.     @Override  
      19.     public String print(String message) {  
      20.         return message;  
      21.     }  
      22.   
      23. }  
      輸出


      然而某一天老板突然下了個指令,不讓打印字母了(不要問為什么...)。實現(xiàn)方案很多,這里用proxy實現(xiàn)。
      1. public class PrintProxy {  
      2.     private Printer printer;  
      3.     public PrintProxy(Printer printer){  
      4.         this.printer = printer;  
      5.     }  
      6.       
      7.     public Printer create(){  
      8.         final Class<?>[] interfaces = new Class[]{Printer.class};  
      9.         final PrinterInvacationHandler handler = new PrinterInvacationHandler(printer);  
      10.           
      11.         return (Printer)Proxy.newProxyInstance(Printer.class.getClassLoader(), interfaces, handler);  
      12.     }  
      13. }  
      14.   
      15. public class PrinterInvacationHandler implements InvocationHandler{  
      16.   
      17.     private final Printer printer;  
      18.     public PrinterInvacationHandler(Printer printer){  
      19.         this.printer = printer;  
      20.     }  
      21.     @Override  
      22.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
      23.         System.out.println("**** before running...");  
      24.         if (method.getName().equals("print") && args.length == 1 && args[0].toString().equals("abc")) {  
      25.             return "打印機無法打印字母:abc";  
      26.         }  
      27.         Object ret = method.invoke(printer, args);  
      28.         System.out.println("**** after running...");  
      29.         return ret;  
      30.     }  
      31. }  
      增加了這個代理以后,Main不要直接打印,而是交由這個代理去管理。
      1. public class Main {  
      2.     public static void main(String[] args) {  
      3.         Printer printer = new HpPrinter();  
      4.         PrintProxy proxy = new PrintProxy(printer);  
      5.           
      6.         Printer proxyOjb = proxy.create();  
      7.         String result1 = proxyOjb.print("abc");  
      8.         String result2 = proxyOjb.print("1234");  
      9.         System.out.println("result1:" + result1);  
      10.         System.out.println("result2:" + result2);  
      11.     }  
      12. }  
      輸出


      可以看到abc被攔截了。Spring AOP,mockito的設(shè)計也是如此。言歸正傳,如果使用mockito。
      Spring service常見的結(jié)構(gòu)是 servie -> dao。當我們測試一個service方法時,mock這個dao的返回。
      1. @Mock  
      2. private xxxDao dao;  
      3. @InjectMocks  
      4. private xxxServiceImpl xxxService;//注意這是實例,不是接口  
      5.   
      6. @Test  
      7. public void test(){  
      8.     MockitoAnnotations.initMocks(this);  
      9.     ModelA a = new ModelA();  
      10.     Mockito.when(dao.methodB(Mockito.anyString())).thenReturn(a);  
      11.     ModelA b = xxxService.methodA("test");  
      12.     //...  
      13. }  


      如果涉及到static,可以引入PowerMockito。下面是個apache validate的例子。
      1. @RunWith(PowerMockRunner.class)  
      2. @PrepareForTest({Validate.class})  
      3. public class xxxMockTest {  
      4.     @Before  
      5.     public void setup(){  
      6.         MockitoAnnotations.initMocks(this);  
      7.         PowerMockito.mockStatic(Validate.class);  
      8.         try {  
      9.             PowerMockito.doNothing().when(Validate.class, "validState",false, "xxx");  
      10.         } catch (Exception e) {  
      11.             // TODO Auto-generated catch block  
      12.             e.printStackTrace();  
      13.         }  
      14.     }  
      15. }  
      再復(fù)雜一些,如果通過static方法調(diào)用時,依賴一個spring bean。
      1. @RunWith(PowerMockRunner.class)  
      2. @PrepareForTest({SpringContextHolder.class})  
      3. public class BaseMockTest {  
      4.     @Before  
      5.     public void setup(){  
      6.         MockitoAnnotations.initMocks(this);  
      7.   
      8.         PowerMockito.mockStatic(SpringContextHolder.class);  
      9.         BDDMockito.given(SpringContextHolder.getBean(xxx.class)).willReturn(new xxxImpl());  
      10.     }  
      11. }  




        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多