我正在Scala中编写ServletUnitTest特性,为ServletUnit提供便利API。我有类似以下内容:
/**
* Utility trait for HttpUnit/ServletUnit tests
*
* @param [T] Type parameter for the class under test
*/
trait ServletUnitTest[T <: HttpServlet] {
/**
* Resource name of the servlet, used to construct the servlet URL.
*/
val servletName: String
/**
* Servlet class under test
*/
implicit val servletClass: Manifest[T]
/**
* ServletUnit {@link ServletRunner}
*/
sealed lazy val servletRunner: ServletRunner = {
val sr = new ServletRunner();
sr.registerServlet(servletName, servletClass.erasure.getName);
sr
}
/**
* A {@link com.meterware.servletunit.ServletUnitClient}
*/
sealed lazy val servletClient = servletRunner.newClient
/**
* The servlet URL, useful for constructing WebRequests
*/
sealed lazy val servletUrl = "http://localhost/" + servletName
def servlet(ic: InvocationContext) = ic.getServlet.asInstanceOf[T]
}
class MyServletTest extends ServletIUnitTest[MyServlet] {
val servletName = "download"
// ... test code ...
}
此代码不按编写方式编译,但希望我的意图很明确。有没有办法做到这一点(有或没有清单)?
答案 0 :(得分:2)
在研究这个主题时,我找到了Jorge Ortiz的解决方案in this scala-list post,它为我做了诀窍,并且比Aaron更简单。 从本质上讲,他的解决方案是(释义):
trait A[T] {
implicit val t: Manifest[T]
}
class B[T: Manifest] extends A[T] {
override val t = manifest[T]
}
(因为我在2011年写这篇文章,我忽略了OP请求为2.7.7兼容...)
答案 1 :(得分:1)
目前,Scala将特征表示为接口,因此该技术可行。但是,这种方法实现traits存在一些问题,因为当将方法添加到trait时,实现类不一定会重新编译,因为接口表示只有一个转发方法指向另一个实际实现该方法的类。为了应对这种情况,今年早些时候有人谈到在运行时使用接口注入到JVM来解决这个问题。如果使用这种方法的权力,那么特征的类型信息将在您捕获之前丢失。
答案 2 :(得分:1)
可以使用Java反射API访问类型信息。它不漂亮,但它有效:
trait A[T]{
def typeParameter = {
val genericType = getClass.getGenericInterfaces()(0).asInstanceOf[ParameterizedType]
genericType.getActualTypeArguments()(0)
}
}
class B extends A[Int]
new B().typeParameter -> java.lang.Integer
应该添加一些不变检查我只实现了快乐的路径。
答案 3 :(得分:0)
我找到了一个有效的解决方案,但它非常尴尬,因为它要求测试类在评估任何trait的lazy val之前调用trait上的方法(clazz)。
/**
* Utility trait for HttpUnit/ServletUnit tests
*
* @param [T] Type parameter for the class under test
*/
trait ServletUnitTest[T <: HttpServlet] {
/**
* Resource name of the servlet, used to construct the servlet URL.
*/
val servletName: String
/**
* Servlet class under test
*/
val servletClass: Class[_] // = clazz
protected def clazz(implicit m: Manifest[T]) = m.erasure
/**
* ServletUnit {@link ServletRunner}
*/
sealed lazy val servletRunner: ServletRunner = {
val sr = new ServletRunner();
sr.registerServlet(servletName, servletClass.getName);
sr
}
/**
* A {@link com.meterware.servletunit.ServletUnitClient}
*/
sealed lazy val servletClient = servletRunner.newClient
/**
* The servlet URL, useful for constructing WebRequests
*/
sealed lazy val servletUrl = "http://localhost/" + servletName
def servlet(ic: InvocationContext) = ic.getServlet.asInstanceOf[T]
}
class MyServletTest extends ServletIUnitTest[MyServlet] {
val servletName = "download"
val servletClass = clazz
// ... test code ...
}