2011年4月6日 星期三

[ Java 文章收集 ] Testing with EasyMock - Tutorial

轉載自 這裡 
EasyMock and Mock Objects Overview : 
接下來的文章必須前提在你知道或用過 JUnit, 如果你對 JUnit 不熟悉的話, 可以先閱讀 JUnit Tutorial 這邊的教學. 

- Testing and Mock Objects 
Unit testing 被定義於與對類別進行測試進行區隔 (Unit test 通常是 Method based). 設計一個Java 類別通常會與其他 Java 類別相依. 而 Mock 物件就像一個 dummy 介面或類別用來當作你要測試方法的參數而該 Mock 物件會有該測試類別預期的行為或回傳值. 而夠過該 Mock 類別物件可以使你在進行測試時不會與外部類別相依. 舉的例子原本你的類別需要透過 DB access 來取的測試資料, 而你可以透過 Mock 類別來提供你的類別所需的測試資料來避免與 DB 的相依性. 
因此你在進行 Unit 測試時, 為了避免連整個 DB 連線都測到, 最好的方法就是透過定義的 Mock 類別提供欲測試的類別來擺脫環境或數據的相依性, 進而獨立的測試你想要測試的方法或類別. 因此有了 Mock framework 來模擬這個過程. 

- EasyMock 
EasyMock 是一個熱門的 Mock framework, 透過它你可以很方便的和 JUnit 進行整合, 底下將會舉一個使用該 Mock framework 的範例進行說明. 

Using Easy Mock and Junit : 
在開始範例前, 先進行環境的設置, 首先到EasyMock 官方網頁下載 EasyMock. 並將 easymock.jar 加入到你的 classpath. 接著還要下載 ASM  cglib 的相依類別並加入到你的 classpath : 
 

接著透過你的 IDE 新建一個 Java Project 並建立以下類別. 而我們將對 IncomeCalculator 類別進行測試, 但該類別相依於另一個介面ICalcMethod 與列舉 EPosition : 

- CalcMethodException.java :
  1. package john.junit.fake.mock.samples;  
  2.   
  3. public class CalcMethodException extends RuntimeException {  
  4.     private static final long serialVersionUID = 1L;  
  5.   
  6.     public CalcMethodException(String message) {  
  7.         super(message);  
  8.     }  
  9. }  

- PositionException.java :
  1. package john.junit.fake.mock.samples;  
  2.   
  3. public class PositionException extends RuntimeException{  
  4.     private static final long serialVersionUID = 1L;  
  5.   
  6.     public PositionException(String message) {  
  7.         super(message);  
  8.     }  
  9. }  

- EPosition.java :
  1. package john.junit.fake.mock.samples;  
  2.   
  3. public enum EPosition {  
  4.     BOSS, PROGRAMMER, SURFER;  
  5. }  

- ICalcMethod.java :
  1. package john.junit.fake.mock.samples;  
  2.   
  3. public interface ICalcMethod {  
  4.     public abstract double calc(EPosition position);  
  5. }  

- IncomeCalculator.java :
  1. package john.junit.fake.mock.samples;  
  2.   
  3. public class IncomeCalculator {  
  4.     private ICalcMethod calcMethod;  
  5.     private EPosition position;  
  6.   
  7.     public void setCalcMethod(ICalcMethod calcMethod){  
  8.         this.calcMethod = calcMethod;  
  9.     }  
  10.     public void setPosition(EPosition position){  
  11.         this.position = position;  
  12.     }  
  13.     public double calc (){  
  14.         if (calcMethod==null){  
  15.             throw new CalcMethodException("CalcMethod not yet maintained");  
  16.         }  
  17.         if (position==null){  
  18.             throw new PositionException("Position not yet maintained");  
  19.         }  
  20.         return calcMethod.calc(position);  
  21.     }  
  22. }  

- IncomeCalculatorTest.java : JUnit 的測試類別, 需要測試的方法要標示 @Test
  1. package john.junit.fake.mock.samples;  
  2.   
  3. import static org.junit.Assert.assertEquals;  
  4. import static org.junit.Assert.fail;  
  5. import org.easymock.EasyMock;  
  6. import org.junit.Before;  
  7. import org.junit.Test;  
  8.   
  9. public class IncomeCalculatorTest {  
  10.     private ICalcMethod calcMethod;  
  11.     private IncomeCalculator calc;  
  12.       
  13.     @Before  
  14.     public void setUp() throws Exception {  
  15.         calcMethod = EasyMock.createMock(ICalcMethod.class);  
  16.         calc = new IncomeCalculator();  
  17.     }  
  18.   
  19.     @Test  
  20.     public void testCalc1() {  
  21.         // Setting up the expected value of the method call calc  
  22.         EasyMock.expect(calcMethod.calc(EPosition.BOSS)).andReturn(70000.0)  
  23.                 .times(2);  
  24.         EasyMock.expect(calcMethod.calc(EPosition.PROGRAMMER))  
  25.                 .andReturn(50000.0);  
  26.         // Setup is finished need to activate the mock  
  27.         EasyMock.replay(calcMethod);  
  28.   
  29.         calc.setCalcMethod(calcMethod);  
  30.         try {  
  31.             calc.calc();  
  32.             fail("Exception did not occur");  
  33.         } catch (PositionException e) {  
  34.   
  35.         }  
  36.         calc.setPosition(EPosition.BOSS);  
  37.         assertEquals((int)70000.0, (int)calc.calc());  
  38.         assertEquals((int)70000.0, (int)calc.calc());  
  39.         calc.setPosition(EPosition.PROGRAMMER);  
  40.         assertEquals((int)50000.0, (int)calc.calc());  
  41.         calc.setPosition(EPosition.SURFER);  
  42.         EasyMock.verify(calcMethod);  
  43.     }  
  44.   
  45.     @Test(expected = CalcMethodException.class)  
  46.     public void testNoCalc() {  
  47.         calc.setPosition(EPosition.SURFER);  
  48.         calc.calc();  
  49.     }  
  50.   
  51.     @Test(expected = PositionException.class)  
  52.     public void testNoPosition() {  
  53.         EasyMock.expect(calcMethod.calc(EPosition.BOSS)).andReturn(70000.0).andThrow(  
  54.                 new PositionException("Unknown Possition")).times(1);  
  55.         EasyMock.replay(calcMethod);  
  56.         calc.setCalcMethod(calcMethod);  
  57.         calc.calc();  
  58.     }  
  59.   
  60.     @Test(expected = PositionException.class)  
  61.     public void testCalc2() {  
  62.         // Setting up the expected value of the method call calc  
  63.         EasyMock.expect(calcMethod.calc(EPosition.SURFER)).andThrow(  
  64.                 new PositionException("Don't know this guy")).times(1);  
  65.   
  66.         // Setup is finished need to activate the mock  
  67.         EasyMock.replay(calcMethod);  
  68.         calc.setPosition(EPosition.SURFER);  
  69.         calc.setCalcMethod(calcMethod);  
  70.         calc.calc();  
  71.     }  
  72. }  

透過方法 expect 告訴 easy mock 去建立對應的方法與該方法需要的參數. 而 andReturn 方法定義了該方法的回傳值. 而方法 times 定義了Mock object 的該方法會被呼叫幾次. 而方法 reply 則告訴framework 建立的 Mock object 生效並於接下來的執行過程透過方法 verify 來確認你的 Mock Object 是否如你預期的被執行, 底下是 JUnit 的執行結果 : 

沒有留言:

張貼留言

[Git 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...