首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 其他教程 > 其他相关 >

应用闭包代替Mock测试

2012-11-05 
使用闭包代替Mock测试在一些简单的测试场景下, 可以采用闭包加as关键字来实现我们需要mock的对象. 这种

使用闭包代替Mock测试
在一些简单的测试场景下, 可以采用闭包加"as"关键字来实现我们需要mock的对象.
这种做法针对"面向接口编程"和"依赖注入"非常有效.
比如有这样的接口:
Java代码
interface Logger { def log(message) }   
interface Helper { def doSomething(param) }   
interface Factory { Helper getInstance() }  

interface Logger { def log(message) }
interface Helper { def doSomething(param) }
interface Factory { Helper getInstance() }
有这样的实现类:
Java代码
class MyApp {   
    private factory   
    private logger   
    MyApp(Factory factory, Logger logger) {   
        this.logger = logger this.factory = factory  
    }   
    def doMyLogic(param) {   
        factory.getInstance().doSomething(param)   
        logger.log('Something done with: ' + param)  
    }  


class MyApp {
    private factory
    private logger
    MyApp(Factory factory, Logger logger) {
        this.logger = logger this.factory = factory
    }
    def doMyLogic(param) {
        factory.getInstance().doSomething(param)
        logger.log('Something done with: ' + param)
    }
}

具体用闭包的写法:
Java代码
def param = 'DUMMY STRING' 
def logger = { message -> assert message == 'Something done with: ' + param}  
def helper = { assert it == param }  
def factory = { helper as Helper }  
def myApp = new MyApp(factory as Factory, logger as Logger)  
myApp.doMyLogic(param) 

def param = 'DUMMY STRING'
def logger = { message -> assert message == 'Something done with: ' + param}
def helper = { assert it == param }
def factory = { helper as Helper }
def myApp = new MyApp(factory as Factory, logger as Logger)
myApp.doMyLogic(param)

只mock一个方法相对来说比较简单, 如果需要同时mock多个方法, 比如这种:
Java代码
interface Helper {  
    def doSomething(param)  
    def doSomethingElse(param)  
}  
    def doMyLogic(param) {  
        def helper = factory.getInstance()  
        helper.doSomething(param)  
        helper.doSomethingElse(param)  
        logger.log('Something done with: ' + param)  
    } 

interface Helper {
    def doSomething(param)
    def doSomethingElse(param)
}
    def doMyLogic(param) {
        def helper = factory.getInstance()
        helper.doSomething(param)
        helper.doSomethingElse(param)
        logger.log('Something done with: ' + param)
    }
对于这种情况, 可以创建一个以要mock的方法为key, 以闭包为value的map, 然后将map as为指定的接口:
Java代码
def helperMethod = { assert it == param }  
def helper = [doSomething:helperMethod, doSomethingElse:helperMethod]  
// as before  
def factory = { helper as Helper } 

def helperMethod = { assert it == param }
def helper = [doSomething:helperMethod, doSomethingElse:helperMethod]
// as before
def factory = { helper as Helper }

如果两个方法执行同样的内容的话, 其实可以这样写:
Java代码
def factory = { helperMethod as Helper } 

def factory = { helperMethod as Helper }

延伸阅读:
http://groovy.codehaus.org/Groovy+way+to+implement+interfaces
这篇文章也不错, 用闭包代替接口

一个接口可以用闭包来实现:
Java代码
// a readable puts chars into a CharBuffer and returns the count of chars added  
def readable = { it.put("12 34".reverse()); 5 } as Readable  
 
// the Scanner constructor can take a Readable  
def s = new Scanner(readable)  
assert s.nextInt() == 43 

// a readable puts chars into a CharBuffer and returns the count of chars added
def readable = { it.put("12 34".reverse()); 5 } as Readable

// the Scanner constructor can take a Readable
def s = new Scanner(readable)
assert s.nextInt() == 43

对于有多个方法的这样定义:
Java代码
interface X  
{ void f(); void g(int n); void h(String s, int n); }  
 
x = {Object[] args -> println "method called with $args"} as X  
x.f()  
x.g(1)  
x.h("hello",2) 

interface X
{ void f(); void g(int n); void h(String s, int n); }

x = {Object[] args -> println "method called with $args"} as X
x.f()
x.g(1)
x.h("hello",2)

那么接口的每一个方法被调用的时候都会执行闭包. 为了适应多个方法, 闭包中的参数使用了数组

更通用的做法是用map来模拟多个方法的情况:
Java代码
impl = [  
  i: 10,  
  hasNext: { impl.i > 0 },  
  next: { impl.i-- },  
]  
iter = impl as Iterator  
while ( iter.hasNext() )  
  println iter.next() 

impl = [
  i: 10,
  hasNext: { impl.i > 0 },
  next: { impl.i-- },
]
iter = impl as Iterator
while ( iter.hasNext() )
  println iter.next()

如果指定的方法为在map中没有对应的key, 那么会抛空指针异常
Java代码
interface X  
{ void f(); void g(int n); void h(String s, int n); }  
 
x = [ f: {println "f called"} ] as X  
x.f()  
//x.g()    // NPE here 

interface X
{ void f(); void g(int n); void h(String s, int n); }

x = [ f: {println "f called"} ] as X
x.f()
//x.g()    // NPE here

下面是一种错误的写法:
Java代码
x = { f: {println "f called"} } as X  
x.f()  
x.g(1) 

x = { f: {println "f called"} } as X
x.f()
x.g(1)
因为这个是定义了一个闭包而不是一个map, 而且从groovy的语法来说也是不允许的.

更多的as用法, 可以看这里(http://johnnyjian.iteye.com/blog/160796)

这里有一点需要注意, as的class类型必须是一个静态的引用, 否则是失败, 但是了动态指定as的具体类型, 也可以使用asType这样写:
Java代码
def loggerInterface = Class.forName( 'my.LoggerInterface' )  
def logger = [  
               log : { Object[] params -> println "LOG: ${params[0]}"; if( params.length > 1 ) params[1].printStackTrace() },  
               close : { println "logger.close called" }  
             ].asType( loggerInterface ) 

热点排行