2013-03-28 213 views
2

With Apache MRUnit我能夠在集羣上運行MapReduce程序之前在本地單元測試我的MapReduce程序。MapReduce單元測試無法模擬DistributedCache.getLocalCacheFiles

我的程序需要從DistributedCache中讀取,所以我把DistributedCache.getLocalCacheFiles換成了我的單元測試中的mock。我設置了一個存根,這樣當該方法不會被調用,而是返回一個本地路徑。但事實證明,該方法被調用並拋出FileNotFoundException

這裏是我的MapReduce程序看起來像

public class TopicByTime implements Tool { 
    private static Map<String, String> topicList = null; 

    public static void main(String[] args) throws Exception { 
    System.exit(ToolRunner.run(new TopicByTime(), args));  
    } 

    @Override 
    public int run(String[] args) throws Exception { 
    Job job = new Job(); 
    /* Job setup */ 
    DistributedCache.addCacheFile(new URI(/* path on hdfs */), conf); 
    job.waitForCompletion(true); 
    return 0; 
    } 

protected static class TimeMapper extends Mapper<LongWritable, Text, Text, Text> {  
    @Override 
    public void setup(Context context) throws IOException, InterruptedException { 
    DistributedCacheClass cache = new DistributedCacheClass(); 
    Path[] localPaths = cache.getLocalCacheFiles(context.getConfiguration()); 
    if (null == localPaths || 0 == localPaths.length) { 
     throw new FileNotFoundException("Distributed cached file not found"); 
    } 
    topicList = Utils.loadTopics(localPaths[0].toString()); 

    } 

    @Override 
    public void map(LongWritable key, Text value, Context context) 
     throws IOException, InterruptedException { 
     /* do map */ 
    } 
    } 

    /* Reducer and overriding methods */ 
} 

而且我的測試程序

public class TestTopicByTime { 

    @Before 
    public void setUp() throws IOException { 
    Path[] localPaths = { new Path("resource/test/topic_by_time.txt")}; 
    Configuration conf = Mockito.mock(Configuration.class); 
    DistributedCacheClass cache = Mockito.mock(DistributedCacheClass.class); 
    when(cache.getLocalCacheFiles(conf)).thenReturn(localPaths); 
    } 

    @Test 
    public void testMapper() { 
    } 

    @Test 
    public void testReducer() { 
    } 

    @Test 
    public void testMapReduce() { 
    } 
} 

DistributedCacheClass是一個簡單的包裝

public class DistributedCacheClass { 
    public Path[] getLocalCacheFiles(Configuration conf) throws IOException { 
    return DistributedCache.getLocalCacheFiles(conf); 
    } 
} 

我可以映射器的設置增加了一個標誌方法,以便在測試時讀取本地路徑,但我確實想分割測試來自我的MapReduce程序的代碼。

我是模擬測試和MRUnit的新手,所以在我的程序中可能會有新手bug。請指出錯誤,我會解決它們,並在下面發佈我的更新。

回答

0

在你TestTopicByTime.setUp()更改的行

when(cache.getLocalCacheFiles(conf)).thenReturn(localPaths); 

when(cache.getLocalCacheFiles(any(Configuration.class))).thenReturn(localPaths); 

雖然嘲諷,參數匹配相等。在你的情況下,它們並不相同,因爲你的代碼中傳遞的實際上下文與你在測試中創建的模擬配置不匹配。所以你需要使用參數匹配器any()來避免確切的對象比較。

編輯: 此外,在您的TimeMapper.setup()你正在創建一個新的緩存, DistributedCacheClass cache = new DistributedCacheClass();

這樣你就不會在所有使用您創建的模擬對象。您必須能夠將您的模擬緩存注入TimeMapper。您可以將外部的緩存對象傳遞給TimeMapper,理想情況是通過構造函數。所以在你的測試中你可以傳遞一個模擬緩存對象。

+0

拋出相同的'FileNotFoundException' – manuzhang 2013-03-28 05:06:40

+0

請參閱我的編輯 – Gopi 2013-03-28 05:12:06

+0

這將混合我的mapreduce程序的測試代碼。那麼爲什麼首先使用嘲諷?如果標誌爲真,我可以通過構造函數傳遞一個標誌並從'setup'的本地路徑中讀取。 – manuzhang 2013-03-28 16:01:21