Create a gist now

Instantly share code, notes, and snippets.

// for the story behind this snippet of code see:
// requires Scala 2.10.0-RC1 or higher
// you also need to have scala-reflect.jar and scala-compiler.jar on your classpath
import scala.reflect.runtime.universe._
object Test extends App {
val f = (r: {val s: String}) => r.s
// we want to build a function `mock` which:
// * takes `fn`, a T => R, with T being a structural type
// * takes `mocker`, a Symbol => Any, which produces a mock value for a given declaration
// * returns the result of invoking `fn` with an argument generated by `mocker`
// example:
// * `fn`: (r: {def s: String}) => r.s
// * `mocker`: sym =>
// * the result will be "s"
def mock[T: TypeTag, R](fn: T => R, mocker: Symbol => Any): R = {
// having annotated T with the TypeTag context bound
// we persist full information about its type to be available at runtime
// afterwards we pattern match against that type using the types declared in the reflection API
// info about the reflection API is available here:
// this stuff required scala-reflect.jar, which means Scala 2.10.0-RC1 or higher
val RefinedType(parents, decls) = typeOf[T]
// we're going to generate a mock argument for `fn` at runtime using the reflection API
// to do that we first create a toolbox, i.e. a runtime Scala compiler
// this functionality requires scala-compiler.jar
// more information about toolboxes is available via the aforementioned link
val tb = runtimeMirror(getClass.getClassLoader).mkToolBox()
// to generate a class with toolboxes, we need to create an abstract syntax tree for it
// see
// to understand how I figured out what exactly is the shape of the tree I need to build
// (for simplicity I assume that the structural type only contains nullary defs)
val emptyCtor = DefDef(NoMods, nme.CONSTRUCTOR, Nil, List(Nil), TypeTree(), Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), Nil)), Literal(Constant(()))))
val classDef = ClassDef(NoMods, newTypeName("Mock"), Nil, Template(parents map TypeTree, emptyValDef, emptyCtor +: => {
val NullaryMethodType(typeOfVal) = decl.typeSignature
// free terms allow to refer to runtime values in the generated code
// creation syntax isn't pretty, but it works
// though we'll most likely improve the syntax in the future
val mockValueOfVal = build.newFreeTerm(, mocker(decl))
build.setTypeSignature(mockValueOfVal, typeOfVal)
// a nasty caveat - if one doesn't provide any flags to the method creator
// (effectively leaving it public by default)
// then the compiler somehow decides to make the method private (probably because this is a local class)
// which leads to NoSuchMethodExceptions later
// therefore we trick the compiler into not touching visibility by making the method protected
// these little things is what makes reflection experimental in 2.10.0
DefDef(Modifiers(Flag.PROTECTED),, Nil, Nil, TypeTree(typeOfVal), Ident(mockValueOfVal))
// now we wrap the class definition into a snippet that instantiates it
// and then use the toolbox to compile and evaluate that snippet
val code = Block(List(classDef), Apply(Select(New(Ident(newTypeName("Mock"))), nme.CONSTRUCTOR), List()))
val mockInstance = tb.eval(code).asInstanceOf[T]
// finally, the invocation
val fn = (r: {def s: String}) => r.s
val result = mock(fn, sym =>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment