Scalatest自定义排序匹配器及Scala sorted排序机制问询
好问题!我来一步步帮你理清这些关于Scalatest排序匹配器和Scala内置排序的疑问:
1. Scalatest中检查排序的匹配器(含自定义/反向排序)
Scalatest确实提供了内置的排序匹配器,同时也完全支持自定义Ordering的场景,包括反向排序:
内置匹配器结合隐式Ordering
默认的be sorted匹配器会自动查找当前作用域中的隐式scala.math.Ordering实例来判断集合是否有序。如果要检查反向排序,只需要提供一个反向的隐式Ordering即可:
import org.scalatest.matchers.should.Matchers._ import scala.math.Ordering val descendingNumbers = List(5, 3, 1) // 提供反向的Int排序规则 implicit val reverseIntOrder: Ordering[Int] = Ordering[Int].reverse descendingNumbers should be sorted // 这会验证列表是否按降序排列
自定义场景的匹配器(比如带时区的时间戳)
对于像“考虑时区的时间戳排序”这类自定义需求,你可以两种方式处理:
- 方式一:在测试块中定义对应的隐式Ordering,直接用
be sortedimport java.time.ZonedDateTime import org.scalatest.matchers.should.Matchers._ import scala.math.Ordering // 按时间戳(绝对时间)排序的Ordering implicit val zonedDateTimeOrder: Ordering[ZonedDateTime] = Ordering.by(_.toInstant) val timeZoneAwareTimes = List( ZonedDateTime.parse("2024-01-01T10:00:00+08:00"), ZonedDateTime.parse("2024-01-01T09:00:00+09:00") // 实际时间更早 ) timeZoneAwareTimes.sorted should be sorted // 验证排序后的结果符合预期 - 方式二:自定义一个显式的匹配器,避免依赖隐式作用域
如果不想依赖隐式,可以自己实现一个匹配器,直接传入自定义Ordering:import org.scalatest.matchers.{Matcher, MatchResult} import scala.math.Ordering def beSortedWith[T](ordering: Ordering[T]): Matcher[Iterable[T]] = new Matcher[Iterable[T]] { override def apply(collection: Iterable[T]): MatchResult = { val elements = collection.toList val isOrdered = elements.zip(elements.tail).forall { case (a, b) => ordering.lteq(a, b) } MatchResult( isOrdered, s"Collection $collection was not sorted using the provided ordering", s"Collection $collection was correctly sorted using the provided ordering" ) } } // 使用示例 val customTimes = List(...) // 你的带时区时间列表 val timeBasedOrder = Ordering.by[ZonedDateTime, Long](_.toInstant.toEpochMilli) customTimes should beSortedWith(timeBasedOrder)
2. Scala中
sorted方法的排序规则 Scala集合的sorted方法完全依赖于scala.math.Ordering的隐式实例,它的核心定义大致如下(以List为例):
def sorted[B >: A](implicit ord: Ordering[B]): List[A]
- 对于Int、String、Double等基础类型,Scala标准库已经提供了默认的隐式Ordering实例,所以可以直接调用
sorted得到自然排序的结果。 - 对于自定义类型,你有两种选择:
- 让自定义类实现
Ordered特质,这样Scala会自动生成对应的Ordering实例:case class User(id: Int) extends Ordered[User] { override def compare(that: User): Int = this.id.compare(that.id) } List(User(3), User(1)).sorted // 会按id升序排列 - 单独定义隐式的Ordering实例,比如针对带时区的时间场景:
case class TimedEvent(occurredAt: ZonedDateTime) implicit val eventOrdering: Ordering[TimedEvent] = Ordering.by(_.occurredAt.toInstant) List(TimedEvent(ZonedDateTime.now()), TimedEvent(ZonedDateTime.now().minusDays(1))).sorted
- 让自定义类实现
至于你说的“查看源码没找到DSL解释器”,其实Scalatest的匹配器是通过隐式转换和特质组合实现的,比如be sorted对应的是SortMatcher类,它在内部会解析当前作用域的隐式Ordering。你可以在Scalatest的Matchers特质中找到相关实现逻辑。
内容的提问来源于stack exchange,提问作者Luciano
相关产品推荐
相关产品推荐

