Scala 如何创建案例类的随机实例?

Scala 如何创建案例类的随机实例?,scala,random,shapeless,random-data,Scala,Random,Shapeless,Random Data,假设我有几个案例类,例如: case class C(c1: Int, c2: Double, c3: Option[String]) case class B(b: Int, cs: Seq[C]) case class A(a: String, bs: Seq[B]) 现在我想为测试生成几个带有随机值的a实例 我正在寻找一种通用的方法来做到这一点。我可能可以通过运行时反射来实现,但我更喜欢编译时解决方案 def randomInstance[A](a: A): A = ??? 我怎么做

假设我有几个案例类,例如:

case class C(c1: Int, c2: Double, c3: Option[String])
case class B(b: Int, cs: Seq[C])
case class A(a: String, bs: Seq[B]) 
现在我想为测试生成几个带有随机值的
a
实例

我正在寻找一种通用的方法来做到这一点。我可能可以通过运行时反射来实现,但我更喜欢编译时解决方案

def randomInstance[A](a: A): A = ???

我怎么做?可以用
无形状的
完成吗?

最简单的方法是使用。您可以通过为实例定义一个
Gen[a]
来实现:

import org.scalacheck.Gen

final case class C(c1: Int, c2: Double, c3: Option[String])
object C {
  val cGen: Gen[C] = for {
    c1 <- Gen.posNum[Int]
    c2 <- Gen.posNum[Double]
    c3 <- Gen.option(Gen.oneOf("foo", "bar", "hello"))
  } yield C(c1, c2, c3)
}
除此之外,您还可以添加一个为您生成
Gen[A]
的完全随机值(您无法控制它们)

你可能还想研究一下(谢谢@Gabriele Petronella),这会进一步简化事情。从文档中:

import com.danielasfregola.randomdatagenerator.RandomDataGenerator

object MyApp extends RandomDataGenerator {

  case class Example(text: String, n: Int)

  val example: Example = random[Example]
  // Example(ਈ䈦㈾钜㔪旅ꪔ墛炝푰⡨䌆ᵅ퍧咪, 73967257)
}

这在以下方面也特别有用。

我们刚刚从scalacheck shapeless转移到使用Scala/Java反射

主要原因是(1)scalacheck shapeless使用宏(编译速度慢),(2)API比我喜欢的要详细一点,(3)生成的值太随意(例如,生成带有日语字符的字符串)

然而,设置它要复杂一些。以下是完整的工作代码,您可以将其复制到代码库中:

import scala.reflect.api
import scala.reflect.api.{TypeCreator, Universe}
import scala.reflect.runtime.universe._

object Maker {
  val mirror = runtimeMirror(getClass.getClassLoader)

  var makerRunNumber = 1

  def apply[T: TypeTag]: T = {
    val method = typeOf[T].companion.decl(TermName("apply")).asMethod
    val params = method.paramLists.head
    val args = params.map { param =>
      makerRunNumber += 1
      param.info match {
        case t if t <:< typeOf[Enumeration#Value] => chooseEnumValue(convert(t).asInstanceOf[TypeTag[_ <: Enumeration]])
        case t if t =:= typeOf[Int] => makerRunNumber
        case t if t =:= typeOf[Long] => makerRunNumber
        case t if t =:= typeOf[Date] => new Date(Time.now.inMillis)
        case t if t <:< typeOf[Option[_]] => None
        case t if t =:= typeOf[String] && param.name.decodedName.toString.toLowerCase.contains("email") => s"random-$arbitrary@give.asia"
        case t if t =:= typeOf[String] => s"arbitrary-$makerRunNumber"
        case t if t =:= typeOf[Boolean] => false
        case t if t <:< typeOf[Seq[_]] => List.empty
        case t if t <:< typeOf[Map[_, _]] => Map.empty
        // Add more special cases here.
        case t if isCaseClass(t) => apply(convert(t))
        case t => throw new Exception(s"Maker doesn't support generating $t")
      }
    }

    val obj = mirror.reflectModule(typeOf[T].typeSymbol.companion.asModule).instance
    mirror.reflect(obj).reflectMethod(method)(args:_*).asInstanceOf[T]
  }

  def chooseEnumValue[E <: Enumeration: TypeTag]: E#Value = {
    val parentType = typeOf[E].asInstanceOf[TypeRef].pre
    val valuesMethod = parentType.baseType(typeOf[Enumeration].typeSymbol).decl(TermName("values")).asMethod
    val obj = mirror.reflectModule(parentType.termSymbol.asModule).instance

    mirror.reflect(obj).reflectMethod(valuesMethod)().asInstanceOf[E#ValueSet].head
  }

  def convert(tpe: Type): TypeTag[_] = {
    TypeTag.apply(
      runtimeMirror(getClass.getClassLoader),
      new TypeCreator {
        override def apply[U <: Universe with Singleton](m: api.Mirror[U]) = {
          tpe.asInstanceOf[U # Type]
        }
      }
    )
  }

  def isCaseClass(t: Type) = {
    t.companion.decls.exists(_.name.decodedName.toString == "apply") &&
      t.decls.exists(_.name.decodedName.toString == "copy")
  }
}
上面的代码生成任意且唯一的值。它们并不是完全随机的。最好在测试中使用


阅读我们在这里的完整博文:

我们已经开始使用Magnolia,它为任意实例的派生提供了比shapeless更快的类型类派生

是要使用的库,下面是一个示例():

case类内部(int:int,str:String)
外壳等级外部(内部:内部)
//ScalaCheck任意
导入magnolify.scalacheck.auto_
导入org.scalacheck.\u//任意[Int]等的隐式实例。
val arb:arbitral[Outer]=隐式[arbitral[Outer]]
任意抽样
//=一些(外部(内部(12345,abcde)))

这也值得一提,它允许你做
val c=random[c]
@GabrielePetronella哦,很好。将其添加到答案中。
scalacheck
has,这应该很有用。
import scala.reflect.api
import scala.reflect.api.{TypeCreator, Universe}
import scala.reflect.runtime.universe._

object Maker {
  val mirror = runtimeMirror(getClass.getClassLoader)

  var makerRunNumber = 1

  def apply[T: TypeTag]: T = {
    val method = typeOf[T].companion.decl(TermName("apply")).asMethod
    val params = method.paramLists.head
    val args = params.map { param =>
      makerRunNumber += 1
      param.info match {
        case t if t <:< typeOf[Enumeration#Value] => chooseEnumValue(convert(t).asInstanceOf[TypeTag[_ <: Enumeration]])
        case t if t =:= typeOf[Int] => makerRunNumber
        case t if t =:= typeOf[Long] => makerRunNumber
        case t if t =:= typeOf[Date] => new Date(Time.now.inMillis)
        case t if t <:< typeOf[Option[_]] => None
        case t if t =:= typeOf[String] && param.name.decodedName.toString.toLowerCase.contains("email") => s"random-$arbitrary@give.asia"
        case t if t =:= typeOf[String] => s"arbitrary-$makerRunNumber"
        case t if t =:= typeOf[Boolean] => false
        case t if t <:< typeOf[Seq[_]] => List.empty
        case t if t <:< typeOf[Map[_, _]] => Map.empty
        // Add more special cases here.
        case t if isCaseClass(t) => apply(convert(t))
        case t => throw new Exception(s"Maker doesn't support generating $t")
      }
    }

    val obj = mirror.reflectModule(typeOf[T].typeSymbol.companion.asModule).instance
    mirror.reflect(obj).reflectMethod(method)(args:_*).asInstanceOf[T]
  }

  def chooseEnumValue[E <: Enumeration: TypeTag]: E#Value = {
    val parentType = typeOf[E].asInstanceOf[TypeRef].pre
    val valuesMethod = parentType.baseType(typeOf[Enumeration].typeSymbol).decl(TermName("values")).asMethod
    val obj = mirror.reflectModule(parentType.termSymbol.asModule).instance

    mirror.reflect(obj).reflectMethod(valuesMethod)().asInstanceOf[E#ValueSet].head
  }

  def convert(tpe: Type): TypeTag[_] = {
    TypeTag.apply(
      runtimeMirror(getClass.getClassLoader),
      new TypeCreator {
        override def apply[U <: Universe with Singleton](m: api.Mirror[U]) = {
          tpe.asInstanceOf[U # Type]
        }
      }
    )
  }

  def isCaseClass(t: Type) = {
    t.companion.decls.exists(_.name.decodedName.toString == "apply") &&
      t.decls.exists(_.name.decodedName.toString == "copy")
  }
}
val user = Maker[User]
val user2 = Maker[User].copy(email = "someemail@email.com")