1

我正在 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 ...
}

这段代码没有按书面形式编译,但希望我的意图很清楚。有没有办法做到这一点(有或没有清单)?

4

4 回答 4

2

在研究这个主题时,我在 Jorge Ortiz的这篇 scala-list 帖子中找到了一个解决方案,它对我有用,而且比 Aaron 的更简单。本质上,他的解决方案是(释义):

  trait A[T] {
    implicit val t: Manifest[T]
  }

  class B[T: Manifest] extends A[T] {
    override val t = manifest[T]
  }

(当我在 2011 年写这篇文章时,我忽略了与 2.7.7 兼容的 OP 请求……)

于 2011-02-15T10:31:13.037 回答
1

目前,Scala 将特征表示为接口,因此这种技术将起作用。然而,这种实现 trait 的方法存在一些问题,当方法被添加到 trait 时,实现类不一定会重新编译,因为接口表示只有一个转发方法,该方法指向实际具体实现该方法的另一个类。作为对此的回应,今年早些时候有人谈到在运行时使用接口注入到 JVM 中来解决这个问题。如果使用这种方法的权力,那么特征的类型信息将在你捕获它之前丢失。

于 2009-12-18T23:23:48.790 回答
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

应该添加一些不变的检查我只实现了快乐的路径。

于 2009-12-22T08:47:35.337 回答
0

我找到了一个可行的解决方案,但它非常尴尬,因为它需要测试类在评估任何 trait 的惰性 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 ...
}
于 2009-12-23T20:47:10.400 回答