{"id":2712,"date":"2018-01-04T13:21:31","date_gmt":"2018-01-04T13:21:31","guid":{"rendered":"https:\/\/davidkeen.com\/blog\/?p=2712"},"modified":"2025-10-23T10:08:49","modified_gmt":"2025-10-22T23:38:49","slug":"loading-test-data-with-play-framework-evolutions","status":"publish","type":"post","link":"https:\/\/davidkeen.com\/blog\/2018\/01\/loading-test-data-with-play-framework-evolutions\/","title":{"rendered":"Loading test data with Play Framework Evolutions"},"content":{"rendered":"\n<p>In a <a href=\"https:\/\/davidkeen.com\/blog\/2016\/09\/loading-test-data-with-scalatest-play\/\">previous article<\/a> I described how to load test data that your ScalaTest Play Framework functional tests might need using Play Framework&#8217;s Evolutions. This made use of the <span class=\"lang:default highlight:0 decode:true crayon-inline \">SimpleEvolutionsReader<\/span> class and defining evolutions in the test setup code.<\/p>\n\n\n\n<p>Recently I wanted to also load some test data from a file and so turned to the <span class=\"lang:default highlight:0 decode:true crayon-inline \">ClassLoaderEvolutionsReader<\/span> class which loads resources from the class path.<\/p>\n\n\n\n<p>The trouble was I wanted to apply the schema from my standard evolution files first and then load the test data. The <span class=\"lang:default highlight:0 decode:true crayon-inline \">ClassLoaderEvolutionsReader<\/span> requires evolutions revisions to start at 1 which would conflict with the standard application evolutions already applied.<\/p>\n\n\n\n<p>So I wrote a custom <span class=\"lang:default highlight:0 decode:true crayon-inline \">SingleRevisionClassLoaderEvolutionsReader<\/span> that reads a single revision from the class path.<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-rich is-provider-embed-handler wp-block-embed-embed-handler\"><div class=\"wp-block-embed__wrapper\">\n<style>.gist table { margin-bottom: 0; }<\/style><div style=\"tab-size: 8\" id=\"gist85087528\" class=\"gist\">\n    <div class=\"gist-file\" translate=\"no\" data-color-mode=\"light\" data-light-theme=\"light\">\n      <div class=\"gist-data\">\n        \n<div class=\"js-gist-file-update-container js-task-list-container\">\n      <div id=\"file-singlerevisionclassloaderevolutionsreader-scala\" class=\"file my-2\">\n    \n    <div itemprop=\"text\"\n      class=\"Box-body p-0 blob-wrapper data type-scala  \"\n      style=\"overflow: auto\" tabindex=\"0\" role=\"region\"\n      aria-label=\"SingleRevisionClassLoaderEvolutionsReader.scala content, created by davidkeen on 01:10PM on January 04, 2018.\"\n    >\n\n        \n<div class=\"js-check-hidden-unicode js-blob-code-container blob-code-content\">\n\n  <template class=\"js-file-alert-template\">\n  <div data-view-component=\"true\" class=\"flash flash-warn flash-full d-flex flex-items-center\">\n  <svg aria-hidden=\"true\" height=\"16\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" data-view-component=\"true\" class=\"octicon octicon-alert\">\n    <path d=\"M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\"><\/path>\n<\/svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\"Link--inTextBlock\" href=\"https:\/\/github.co\/hiddenchars\" target=\"_blank\">Learn more about bidirectional Unicode characters<\/a>\n    <\/span>\n\n\n  <div data-view-component=\"true\" class=\"flash-action\">        <a href=\"{{ revealButtonHref }}\" data-view-component=\"true\" class=\"btn-sm btn\">    Show hidden characters\n<\/a>\n<\/div>\n<\/div><\/template>\n<template class=\"js-line-alert-template\">\n  <span aria-label=\"This line has hidden Unicode characters\" data-view-component=\"true\" class=\"line-alert tooltipped tooltipped-e\">\n    <svg aria-hidden=\"true\" height=\"16\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" data-view-component=\"true\" class=\"octicon octicon-alert\">\n    <path d=\"M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\"><\/path>\n<\/svg>\n<\/span><\/template>\n\n  <table data-hpc class=\"highlight tab-size js-file-line-container\" data-tab-size=\"4\" data-paste-markdown-skip data-tagsearch-path=\"SingleRevisionClassLoaderEvolutionsReader.scala\">\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L1\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"1\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC1\" class=\"blob-code blob-code-inner js-file-line\">import play.api.db.evolutions.{ClassLoaderEvolutionsReader, Evolution}<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L2\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"2\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC2\" class=\"blob-code blob-code-inner js-file-line\">import play.api.libs.Collections<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L3\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"3\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC3\" class=\"blob-code blob-code-inner js-file-line\">\n<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L4\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"4\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC4\" class=\"blob-code blob-code-inner js-file-line\">\/***<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L5\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"5\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC5\" class=\"blob-code blob-code-inner js-file-line\">  * Evolutions reader that reads a single revision from the class path.<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L6\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"6\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC6\" class=\"blob-code blob-code-inner js-file-line\">  *<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L7\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"7\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC7\" class=\"blob-code blob-code-inner js-file-line\">  * @param revision the revision number to load<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L8\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"8\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC8\" class=\"blob-code blob-code-inner js-file-line\">  * @param prefix A prefix that gets added to the resource file names<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L9\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"9\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC9\" class=\"blob-code blob-code-inner js-file-line\">  *\/<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L10\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"10\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC10\" class=\"blob-code blob-code-inner js-file-line\">class SingleRevisionClassLoaderEvolutionsReader(val revision: Int, val prefix: String) extends ClassLoaderEvolutionsReader(prefix = prefix) {<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L11\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"11\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC11\" class=\"blob-code blob-code-inner js-file-line\">\n<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L12\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"12\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC12\" class=\"blob-code blob-code-inner js-file-line\">  override def evolutions(db: String): Seq[Evolution] = {<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L13\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"13\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC13\" class=\"blob-code blob-code-inner js-file-line\">    val upsMarker = &quot;&quot;&quot;^#.*!Ups.*$&quot;&quot;&quot;.r<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L14\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"14\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC14\" class=\"blob-code blob-code-inner js-file-line\">    val downsMarker = &quot;&quot;&quot;^#.*!Downs.*$&quot;&quot;&quot;.r<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L15\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"15\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC15\" class=\"blob-code blob-code-inner js-file-line\">\n<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L16\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"16\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC16\" class=\"blob-code blob-code-inner js-file-line\">    val UPS = &quot;UPS&quot;<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L17\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"17\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC17\" class=\"blob-code blob-code-inner js-file-line\">    val DOWNS = &quot;DOWNS&quot;<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L18\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"18\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC18\" class=\"blob-code blob-code-inner js-file-line\">    val UNKNOWN = &quot;UNKNOWN&quot;<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L19\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"19\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC19\" class=\"blob-code blob-code-inner js-file-line\">\n<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L20\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"20\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC20\" class=\"blob-code blob-code-inner js-file-line\">    val mapUpsAndDowns: PartialFunction[String, String] = {<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L21\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"21\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC21\" class=\"blob-code blob-code-inner js-file-line\">      case upsMarker() =&gt; UPS<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L22\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"22\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC22\" class=\"blob-code blob-code-inner js-file-line\">      case downsMarker() =&gt; DOWNS<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L23\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"23\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC23\" class=\"blob-code blob-code-inner js-file-line\">      case _ =&gt; UNKNOWN<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L24\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"24\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC24\" class=\"blob-code blob-code-inner js-file-line\">    }<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L25\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"25\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC25\" class=\"blob-code blob-code-inner js-file-line\">\n<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L26\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"26\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC26\" class=\"blob-code blob-code-inner js-file-line\">    val isMarker: PartialFunction[String, Boolean] = {<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L27\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"27\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC27\" class=\"blob-code blob-code-inner js-file-line\">      case upsMarker() =&gt; true<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L28\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"28\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC28\" class=\"blob-code blob-code-inner js-file-line\">      case downsMarker() =&gt; true<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L29\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"29\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC29\" class=\"blob-code blob-code-inner js-file-line\">      case _ =&gt; false<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L30\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"30\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC30\" class=\"blob-code blob-code-inner js-file-line\">    }<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L31\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"31\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC31\" class=\"blob-code blob-code-inner js-file-line\">\n<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L32\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"32\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC32\" class=\"blob-code blob-code-inner js-file-line\">    loadResource(db, revision).map { stream =&gt;<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L33\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"33\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC33\" class=\"blob-code blob-code-inner js-file-line\">      val script = scala.io.Source.fromInputStream(stream).mkString<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L34\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"34\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC34\" class=\"blob-code blob-code-inner js-file-line\">\n<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L35\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"35\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC35\" class=\"blob-code blob-code-inner js-file-line\">      val parsed = Collections.unfoldLeft((&quot;&quot;, script.split(&#39;\\n&#39;).toList.map(_.trim))) {<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L36\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"36\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC36\" class=\"blob-code blob-code-inner js-file-line\">        case (_, Nil) =&gt; None<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L37\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"37\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC37\" class=\"blob-code blob-code-inner js-file-line\">        case (context, lines) =&gt; {<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L38\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"38\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC38\" class=\"blob-code blob-code-inner js-file-line\">          val (some, next) = lines.span(l =&gt; !isMarker(l))<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L39\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"39\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC39\" class=\"blob-code blob-code-inner js-file-line\">          Some((next.headOption.map(c =&gt; (mapUpsAndDowns(c), next.tail)).getOrElse(&quot;&quot; -&gt; Nil),<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L40\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"40\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC40\" class=\"blob-code blob-code-inner js-file-line\">            context -&gt; some.mkString(&quot;\\n&quot;)))<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L41\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"41\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC41\" class=\"blob-code blob-code-inner js-file-line\">        }<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L42\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"42\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC42\" class=\"blob-code blob-code-inner js-file-line\">      }.reverse.drop(1).groupBy(i =&gt; i._1).mapValues { _.map(_._2).mkString(&quot;\\n&quot;).trim }<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L43\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"43\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC43\" class=\"blob-code blob-code-inner js-file-line\">\n<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L44\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"44\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC44\" class=\"blob-code blob-code-inner js-file-line\">      Evolution(<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L45\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"45\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC45\" class=\"blob-code blob-code-inner js-file-line\">        revision,<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L46\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"46\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC46\" class=\"blob-code blob-code-inner js-file-line\">        parsed.getOrElse(UPS, &quot;&quot;),<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L47\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"47\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC47\" class=\"blob-code blob-code-inner js-file-line\">        parsed.getOrElse(DOWNS, &quot;&quot;))<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L48\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"48\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC48\" class=\"blob-code blob-code-inner js-file-line\">    }.toList<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L49\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"49\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC49\" class=\"blob-code blob-code-inner js-file-line\">  }<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L50\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"50\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC50\" class=\"blob-code blob-code-inner js-file-line\">}<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L51\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"51\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC51\" class=\"blob-code blob-code-inner js-file-line\">\n<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L52\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"52\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC52\" class=\"blob-code blob-code-inner js-file-line\">object SingleRevisionClassLoaderEvolutionsReader {<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L53\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"53\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC53\" class=\"blob-code blob-code-inner js-file-line\">  def apply(revision: Int, prefix: String = &quot;&quot;) = new SingleRevisionClassLoaderEvolutionsReader(revision, prefix)<\/td>\n        <\/tr>\n        <tr>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-L54\" class=\"blob-num js-line-number js-blob-rnum\" data-line-number=\"54\"><\/td>\n          <td id=\"file-singlerevisionclassloaderevolutionsreader-scala-LC54\" class=\"blob-code blob-code-inner js-file-line\">}<\/td>\n        <\/tr>\n  <\/table>\n<\/div>\n\n\n    <\/div>\n\n  <\/div>\n\n<\/div>\n\n      <\/div>\n      <div class=\"gist-meta\">\n        <a href=\"https:\/\/gist.github.com\/davidkeen\/df83d5716bb72073df4fb5834f727d2d\/raw\/6ebe508e9047645cd6a48a2d9956f049eec37f8f\/SingleRevisionClassLoaderEvolutionsReader.scala\" style=\"float:right\" class=\"Link--inTextBlock\">view raw<\/a>\n        <a href=\"https:\/\/gist.github.com\/davidkeen\/df83d5716bb72073df4fb5834f727d2d#file-singlerevisionclassloaderevolutionsreader-scala\" class=\"Link--inTextBlock\">\n          SingleRevisionClassLoaderEvolutionsReader.scala\n        <\/a>\n        hosted with &#10084; by <a class=\"Link--inTextBlock\" href=\"https:\/\/github.com\">GitHub<\/a>\n      <\/div>\n    <\/div>\n<\/div>\n\n<\/div><\/figure>\n\n\n\n<p>You can then place your evolution files in <span class=\"lang:default highlight:0 decode:true crayon-inline\">\/test\/resources\/evolutions\/default\/<\/span> and apply them after your standard evolutions in your test setup, for example, if your test data was in a file called <span class=\"lang:default highlight:0 decode:true crayon-inline\">100.sql<\/span> :<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>\/\/ Load the database schema\nEvolutions.applyEvolutions(db)\n\n\/\/ Load the test data\nEvolutions.applyEvolutions(db, SingleRevisionClassLoaderEvolutionsReader(revision = 100))<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #616E88\">\/\/ Load the database schema<\/span><\/span>\n<span class=\"line\"><span style=\"color: #8FBCBB\">Evolutions<\/span><span style=\"color: #D8DEE9FF\">.applyEvolutions(db)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #616E88\">\/\/ Load the test data<\/span><\/span>\n<span class=\"line\"><span style=\"color: #8FBCBB\">Evolutions<\/span><span style=\"color: #D8DEE9FF\">.applyEvolutions(db, <\/span><span style=\"color: #8FBCBB\">SingleRevisionClassLoaderEvolutionsReader<\/span><span style=\"color: #D8DEE9FF\">(revision <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">100<\/span><span style=\"color: #D8DEE9FF\">))<\/span><\/span><\/code><\/pre><\/div>\n","protected":false},"excerpt":{"rendered":"<p>In a previous article I described how to load test data that your ScalaTest Play Framework functional tests might need using Play Framework&#8217;s Evolutions. This made use of the SimpleEvolutionsReader class and defining evolutions in the test setup code. Recently I wanted to also load some test data from a file and so turned to [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":2715,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[25],"tags":[],"class_list":["post-2712","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dev"],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/davidkeen.com\/blog\/wp-content\/uploads\/2018\/01\/port-crane-harbour-crane-envelope-162612.jpeg?fit=1280%2C853&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/davidkeen.com\/blog\/wp-json\/wp\/v2\/posts\/2712","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/davidkeen.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/davidkeen.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/davidkeen.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/davidkeen.com\/blog\/wp-json\/wp\/v2\/comments?post=2712"}],"version-history":[{"count":9,"href":"https:\/\/davidkeen.com\/blog\/wp-json\/wp\/v2\/posts\/2712\/revisions"}],"predecessor-version":[{"id":2839,"href":"https:\/\/davidkeen.com\/blog\/wp-json\/wp\/v2\/posts\/2712\/revisions\/2839"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/davidkeen.com\/blog\/wp-json\/wp\/v2\/media\/2715"}],"wp:attachment":[{"href":"https:\/\/davidkeen.com\/blog\/wp-json\/wp\/v2\/media?parent=2712"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/davidkeen.com\/blog\/wp-json\/wp\/v2\/categories?post=2712"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/davidkeen.com\/blog\/wp-json\/wp\/v2\/tags?post=2712"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}