Repository: 7mind/izumi
Pull request #959 (2020-03-16 19:37:31)

distage-testkit-scalatest: Fix compatibility with Scalatest matchers and Scalamock's MockFactory
* Fix a bug where tests with parameters were ignored when the test effect type was `Identity`, had to restricting effectful test overloads to `F[Unit]` & `F[Assertion]`
* Move SafeTestReporter out of org.scalatest (does not access private functions)
* Move `org.scalatest.*` classes that access private scalatest functions to `org.scalatest.distage` to avoid clashes


Patch:
diff --git a/build.sbt b/build.sbt
index 313a94446..5b274fdd7 100644
--- a/build.sbt
+++ b/build.sbt
@@ -1758,6 +1758,7 @@ lazy val `distage-testkit-scalatest` = project.in(file("distage/distage-testkit-
       "org.typelevel" %% "cats-core" % V.cats % Optional,
       "org.typelevel" %% "cats-effect" % V.cats_effect % Optional,
       "dev.zio" %% "zio" % V.zio % Optional,
+      "org.scalamock" %% "scalamock" % V.scalamock % Test,
       "org.scalatest" %% "scalatest" % V.scalatest
     )
   )
diff --git a/project/Deps.sc b/project/Deps.sc
index 2f2f481d1..d0804374d 100644
--- a/project/Deps.sc
+++ b/project/Deps.sc
@@ -25,6 +25,7 @@ object Izumi {
     val typesafe_config = Version.VExpr("V.typesafe_config")
     val cglib_nodep = Version.VExpr("V.cglib_nodep")
     val scala_java_time = Version.VExpr("V.scala_java_time")
+    val scalamock = Version.VExpr("V.scalamock")
     val docker_java = Version.VExpr("V.docker_java")
   }
 
@@ -106,6 +107,7 @@ object Izumi {
 
     final val fast_classpath_scanner = Library("io.github.classgraph", "classgraph", V.classgraph, LibraryType.Invariant) in Scope.Compile.jvm
     final val scala_java_time = Library("io.github.cquiroz", "scala-java-time", V.scala_java_time, LibraryType.Auto) in Scope.Compile.js
+    final val scalamock = Library("org.scalamock", "scalamock", V.scalamock, LibraryType.Auto)
 
     final val slf4j_api = Library("org.slf4j", "slf4j-api", V.slf4j, LibraryType.Invariant) in Scope.Compile.jvm
     final val slf4j_simple = Library("org.slf4j", "slf4j-simple", V.slf4j, LibraryType.Invariant) in Scope.Test.jvm
@@ -494,7 +496,10 @@ object Izumi {
       ),
       Artifact(
         name = Projects.distage.testkitScalatest,
-        libs = allMonadsOptional ++ Seq(scalatest.dependency).map(_ in Scope.Compile.all),
+        libs = allMonadsOptional ++ Seq(
+          scalamock in Scope.Test.all,
+          scalatest.dependency in Scope.Compile.all,
+        ),
         depends =
           Seq(Projects.distage.testkitCore).map(_ in Scope.Compile.all) ++
             Seq(Projects.distage.core, Projects.distage.plugins).map(_ tin Scope.Compile.all),
diff --git a/project/Versions.scala b/project/Versions.scala
index 23a5f1a92..7fda01af7 100644
--- a/project/Versions.scala
+++ b/project/Versions.scala
@@ -21,6 +21,10 @@ object V {
 
   val http4s = "0.21.1"
 
+  // good to drop - scala
+  val scala_java_time = "2.0.0-RC3"
+  val scalamock = "4.4.0"
+
   // java-only dependencies below
   val classgraph = "4.8.65"
   val slf4j = "1.7.30"
@@ -28,6 +32,5 @@ object V {
 
   // good to drop - java
   val cglib_nodep = "3.3.0"
-  val scala_java_time = "2.0.0-RC3"
   val docker_java = "3.2.1"
 }


Test patch:
diff --git a/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/services/dstest/DistageTestRunner.scala b/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/services/dstest/DistageTestRunner.scala
index e7d807a43..8da766635 100644
--- a/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/services/dstest/DistageTestRunner.scala
+++ b/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/services/dstest/DistageTestRunner.scala
@@ -90,7 +90,8 @@ class DistageTestRunner[F[_]: TagK]
           distageTest =>
             val keys = distageTest.test.get.diKeys.toSet
             val testRoots = keys ++ env.forcedRoots
-            distageTest -> injector.plan(PlannerInput(appModule, testRoots))
+            val plan = if (testRoots.nonEmpty) injector.plan(PlannerInput(appModule, testRoots)) else OrderedPlan.empty
+            distageTest -> plan
         }
 
         val wholeEnvKeys = testPlans.flatMap(_._2.steps).map(_.target).toSet
diff --git a/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/scalatest/AssertBIO.scala b/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/scalatest/AssertBIO.scala
index 5a663babe..03d45a61e 100644
--- a/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/scalatest/AssertBIO.scala
+++ b/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/scalatest/AssertBIO.scala
@@ -3,7 +3,8 @@ package izumi.distage.testkit.scalatest
 import izumi.distage.testkit.scalatest.AssertBIO.AssertBIOMacro
 import izumi.functional.bio.BIO
 import org.scalactic.{Prettifier, source}
-import org.scalatest.{Assertion, DistageAssertionsMacro}
+import org.scalatest.Assertion
+import org.scalatest.distage.DistageAssertionsMacro
 
 import scala.language.experimental.macros
 import scala.reflect.macros.blackbox
diff --git a/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/scalatest/AssertCIO.scala b/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/scalatest/AssertCIO.scala
index b618bc5e6..a2602055b 100644
--- a/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/scalatest/AssertCIO.scala
+++ b/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/scalatest/AssertCIO.scala
@@ -4,7 +4,8 @@ import cats.effect.IO
 import izumi.distage.testkit.scalatest.AssertCIO.AssertCIOMacro
 import org.scalactic.Prettifier
 import org.scalactic.source.Position
-import org.scalatest.{Assertion, DistageAssertionsMacro}
+import org.scalatest.Assertion
+import org.scalatest.distage.DistageAssertionsMacro
 
 import scala.language.experimental.macros
 import scala.reflect.macros.blackbox
diff --git a/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/scalatest/AssertIO.scala b/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/scalatest/AssertIO.scala
index 2f80778db..5a857008e 100644
--- a/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/scalatest/AssertIO.scala
+++ b/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/scalatest/AssertIO.scala
@@ -3,7 +3,8 @@ package izumi.distage.testkit.scalatest
 import izumi.distage.testkit.scalatest.AssertIO.AssertIOMacro
 import org.scalactic.Prettifier
 import org.scalactic.source.Position
-import org.scalatest.{Assertion, DistageAssertionsMacro}
+import org.scalatest.Assertion
+import org.scalatest.distage.DistageAssertionsMacro
 import zio.IO
 
 import scala.language.experimental.macros
diff --git a/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/scalatest/DistageBIOSpecScalatest.scala b/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/scalatest/DistageBIOSpecScalatest.scala
index 7e462c4cc..1a999ce1d 100644
--- a/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/scalatest/DistageBIOSpecScalatest.scala
+++ b/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/scalatest/DistageBIOSpecScalatest.scala
@@ -3,7 +3,7 @@ package izumi.distage.testkit.scalatest
 import distage.TagKK
 import izumi.distage.testkit.services.scalatest.dstest.DistageAbstractScalatestSpec
 import izumi.distage.testkit.services.scalatest.dstest.DistageAbstractScalatestSpec.{DSWordSpecStringWrapper, DSWordSpecStringWrapper2}
-import org.scalatest.DistageScalatestTestSuiteRunner
+import org.scalatest.distage.DistageScalatestTestSuiteRunner
 
 import scala.language.implicitConversions
 
@@ -11,7 +11,7 @@ abstract class DistageBIOSpecScalatest[F[+_, +_]](implicit val tagBIO: TagKK[F])
   extends DistageScalatestTestSuiteRunner[F[Throwable, ?]]
     with DistageAbstractScalatestSpec[F[Throwable, ?]] {
 
-  protected implicit def convertToWordSpecStringWrapper2(s: String): DSWordSpecStringWrapper2[F] = {
+  protected implicit def convertToWordSpecStringWrapperDS2(s: String): DSWordSpecStringWrapper2[F] = {
     new DSWordSpecStringWrapper2(context, distageSuiteName, distageSuiteId, s, this, testEnv)
   }
 
diff --git a/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/scalatest/DistageSpecScalatest.scala b/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/scalatest/DistageSpecScalatest.scala
index 88e187b34..7076acab1 100644
--- a/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/scalatest/DistageSpecScalatest.scala
+++ b/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/scalatest/DistageSpecScalatest.scala
@@ -2,7 +2,7 @@ package izumi.distage.testkit.scalatest
 
 import distage.TagK
 import izumi.distage.testkit.services.scalatest.dstest.DistageAbstractScalatestSpec
-import org.scalatest.DistageScalatestTestSuiteRunner
+import org.scalatest.distage.DistageScalatestTestSuiteRunner
 
 abstract class DistageSpecScalatest[F[_]: TagK]
   extends DistageScalatestTestSuiteRunner[F]
diff --git a/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/services/scalatest/dstest/DistageAbstractScalatestSpec.scala b/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/services/scalatest/dstest/DistageAbstractScalatestSpec.scala
index aea631362..fea7553fa 100644
--- a/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/services/scalatest/dstest/DistageAbstractScalatestSpec.scala
+++ b/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/services/scalatest/dstest/DistageAbstractScalatestSpec.scala
@@ -8,10 +8,12 @@ import izumi.distage.testkit.services.dstest.DistageTestRunner.{DistageTest, Tes
 import izumi.distage.testkit.services.dstest._
 import izumi.distage.testkit.services.scalatest.dstest.DistageAbstractScalatestSpec._
 import izumi.distage.testkit.services.{DISyntaxBIOBase, DISyntaxBase}
-import izumi.fundamentals.platform.language.{CodePosition, CodePositionMaterializer, Quirks, unused}
+import izumi.fundamentals.platform.language.{CodePosition, CodePositionMaterializer, unused}
 import izumi.logstage.api.{IzLogger, Log}
 import org.scalactic.source
-import org.scalatest.{Assertion, TestCancellation}
+import org.scalatest.Assertion
+import org.scalatest.distage.TestCancellation
+import org.scalatest.verbs.{CanVerb, MustVerb, ShouldVerb, StringVerbBlockRegistration}
 
 import scala.language.implicitConversions
 
@@ -25,8 +27,9 @@ trait WithSingletonTestRegistration[F[_]] extends AbstractDistageSpec[F] {
   }
 }
 
+@org.scalatest.Finders(value = Array("org.scalatest.finders.WordSpecFinder"))
 trait DistageAbstractScalatestSpec[F[_]]
-  extends ScalatestWords
+  extends ShouldVerb with MustVerb with CanVerb
     with DistageTestEnv
     with WithSingletonTestRegistration[F] {
   this: AbstractDistageSpec[F] =>
@@ -42,15 +45,19 @@ trait DistageAbstractScalatestSpec[F[_]]
   protected def distageSuiteId: String = this.getClass.getName
 
   //
-  protected[distage] var context: Option[SuiteContext] = None
+  private[distage] var context: Option[SuiteContext] = None
 
-  override def registerBranch(description: String, childPrefix: Option[String], verb: String, methodName: String, stackDepth: Int, adjustment: Int, pos: source.Position, fun: () => Unit): Unit = {
-    Quirks.discard(childPrefix, methodName, stackDepth, adjustment, pos)
+  implicit val subjectRegistrationFunction1: StringVerbBlockRegistration = new StringVerbBlockRegistration {
+    override def apply(left: String, verb: String, @unused pos: source.Position, f: () => Unit): Unit = {
+      registerBranch(left, verb, f)
+    }
+  }
+
+  protected def registerBranch(description: String, verb: String, fun: () => Unit): Unit = {
     this.context = Some(SuiteContext(description, verb))
     fun()
     this.context = None
   }
-  //
 
   protected implicit def convertToWordSpecStringWrapperDS(s: String): DSWordSpecStringWrapper[F] = {
     new DSWordSpecStringWrapper(context, distageSuiteName, distageSuiteId, s, this, testEnv)
@@ -64,6 +71,24 @@ object DistageAbstractScalatestSpec {
     }
   }
 
+  trait LowPriorityIdentityOverloads[F[_]] extends DISyntaxBase[F] {
+    def in(function: ProviderMagnet[Unit])(implicit pos: CodePositionMaterializer, d1: DummyImplicit, d2: DummyImplicit): Unit = {
+      takeAny(function, pos.get)
+    }
+
+    def in(function: ProviderMagnet[Assertion])(implicit pos: CodePositionMaterializer, d1: DummyImplicit, d2: DummyImplicit, d3: DummyImplicit): Unit = {
+      takeAny(function, pos.get)
+    }
+
+    def in(value: => Unit)(implicit pos: CodePositionMaterializer, d1: DummyImplicit, d2: DummyImplicit): Unit = {
+      takeAny(() => value, pos.get)
+    }
+
+    def in(value: => Assertion)(implicit pos: CodePositionMaterializer, d1: DummyImplicit, d2: DummyImplicit, d3: DummyImplicit): Unit = {
+      takeAny(() => value, pos.get)
+    }
+  }
+
   class DSWordSpecStringWrapper[F[_]](
                                        context: Option[SuiteContext],
                                        suiteName: String,
@@ -73,7 +98,7 @@ object DistageAbstractScalatestSpec {
                                        env: TestEnvironment,
                                      )(
                                        implicit override val tagMonoIO: TagK[F],
-                                     ) extends DISyntaxBase[F] {
+                                     ) extends DISyntaxBase[F] with LowPriorityIdentityOverloads[F] {
 
     override protected def takeIO(function: ProviderMagnet[F[_]], pos: CodePosition): Unit = {
       val id = TestId(
@@ -85,51 +110,23 @@ object DistageAbstractScalatestSpec {
       reg.registerTest(function, env, pos, id)
     }
 
-    def in(function: ProviderMagnet[F[_]])(implicit pos: CodePositionMaterializer): Unit = {
+    def in(function: ProviderMagnet[F[Unit]])(implicit pos: CodePositionMaterializer): Unit = {
       takeIO(function, pos.get)
     }
 
-    def in(function: ProviderMagnet[Unit])(implicit pos: CodePositionMaterializer, dummyImplicit: DummyImplicit): Unit = {
-      takeAny(function, pos.get)
-    }
-
-    def in(function: ProviderMagnet[Assertion])(implicit pos: CodePositionMaterializer, dummyImplicit: DummyImplicit, dummyImplicit2: DummyImplicit): Unit = {
-      takeAny(function, pos.get)
+    def in(function: ProviderMagnet[F[Assertion]])(implicit pos: CodePositionMaterializer, d1: DummyImplicit): Unit = {
+      takeIO(function, pos.get)
     }
 
-    def in(value: => F[_])(implicit pos: CodePositionMaterializer): Unit = {
+    def in(value: => F[Unit])(implicit pos: CodePositionMaterializer): Unit = {
       takeIO(() => value, pos.get)
     }
 
-    def in(value: => Unit)(implicit pos: CodePositionMaterializer, dummyImplicit: DummyImplicit): Unit = {
-      takeAny(() => value, pos.get)
-    }
-
-    def in(value: => Assertion)(implicit pos: CodePositionMaterializer, dummyImplicit: DummyImplicit, dummyImplicit2: DummyImplicit): Unit = {
-      takeAny(() => value, pos.get)
-    }
-
-    def skip(@unused function: ProviderMagnet[F[_]])(implicit pos: CodePositionMaterializer): Unit = {
-      takeFunIO(cancel, pos.get)
-    }
-
-    def skip(@unused function: ProviderMagnet[Unit])(implicit pos: CodePositionMaterializer, dummyImplicit: DummyImplicit): Unit = {
-      takeFunIO(cancel, pos.get)
-    }
-
-    def skip(@unused function: ProviderMagnet[Assertion])(implicit pos: CodePositionMaterializer, dummyImplicit: DummyImplicit, dummyImplicit2: DummyImplicit): Unit = {
-      takeFunIO(cancel, pos.get)
-    }
-
-    def skip(@unused value: => F[_])(implicit pos: CodePositionMaterializer): Unit = {
-      takeFunIO(cancel, pos.get)
-    }
-
-    def skip(@unused value: => Unit)(implicit pos: CodePositionMaterializer, dummyImplicit: DummyImplicit): Unit = {
-      takeFunIO(cancel, pos.get)
+    def in(value: => F[Assertion])(implicit pos: CodePositionMaterializer, d1: DummyImplicit): Unit = {
+      takeIO(() => value, pos.get)
     }
 
-    def skip(@unused value: => Assertion)(implicit pos: CodePositionMaterializer, dummyImplicit: DummyImplicit, dummyImplicit2: DummyImplicit): Unit = {
+    def skip(@unused value: => Any)(implicit pos: CodePositionMaterializer): Unit = {
       takeFunIO(cancel, pos.get)
     }
 
@@ -151,7 +148,7 @@ object DistageAbstractScalatestSpec {
                                              env: TestEnvironment,
                                            )(
                                              implicit override val tagBIO: TagKK[F],
-                                           ) extends DISyntaxBIOBase[F] {
+                                           ) extends DISyntaxBIOBase[F] with LowPriorityIdentityOverloads[F[Throwable, ?]] {
     override val tagMonoIO: TagK[F[Throwable, ?]] = TagK[F[Throwable, ?]]
 
     override protected def takeIO(fAsThrowable: ProviderMagnet[F[Throwable, _]], pos: CodePosition): Unit = {
@@ -164,51 +161,23 @@ object DistageAbstractScalatestSpec {
       reg.registerTest(fAsThrowable, env, pos, id)
     }
 
-    def in(function: ProviderMagnet[F[_, _]])(implicit pos: CodePositionMaterializer): Unit = {
+    def in(function: ProviderMagnet[F[_, Unit]])(implicit pos: CodePositionMaterializer): Unit = {
       takeBIO(function, pos.get)
     }
 
-    def in(function: ProviderMagnet[Unit])(implicit pos: CodePositionMaterializer, dummyImplicit: DummyImplicit): Unit = {
-      takeAny(function, pos.get)
-    }
-
-    def in(function: ProviderMagnet[Assertion])(implicit pos: CodePositionMaterializer, dummyImplicit: DummyImplicit, dummyImplicit2: DummyImplicit): Unit = {
-      takeAny(function, pos.get)
+    def in(function: ProviderMagnet[F[_, Assertion]])(implicit pos: CodePositionMaterializer, d1: DummyImplicit): Unit = {
+      takeBIO(function, pos.get)
     }
 
-    def in(value: => F[_, _])(implicit pos: CodePositionMaterializer): Unit = {
+    def in(value: => F[_, Unit])(implicit pos: CodePositionMaterializer): Unit = {
       takeBIO(() => value, pos.get)
     }
 
-    def in(value: => Unit)(implicit pos: CodePositionMaterializer, dummyImplicit: DummyImplicit): Unit = {
-      takeAny(() => value, pos.get)
-    }
-
-    def in(value: => Assertion)(implicit pos: CodePositionMaterializer, dummyImplicit: DummyImplicit, dummyImplicit2: DummyImplicit): Unit = {
-      takeAny(() => value, pos.get)
-    }
-
-    def skip(@unused function: ProviderMagnet[F[_, _]])(implicit pos: CodePositionMaterializer): Unit = {
-      takeFunIO(cancel, pos.get)
-    }
-
-    def skip(@unused function: ProviderMagnet[Unit])(implicit pos: CodePositionMaterializer, dummyImplicit: DummyImplicit): Unit = {
-      takeFunIO(cancel, pos.get)
-    }
-
-    def skip(@unused function: ProviderMagnet[Assertion])(implicit pos: CodePositionMaterializer, dummyImplicit: DummyImplicit, dummyImplicit2: DummyImplicit): Unit = {
-      takeFunIO(cancel, pos.get)
-    }
-
-    def skip(@unused value: => F[_, _])(implicit pos: CodePositionMaterializer): Unit = {
-      takeFunIO(cancel, pos.get)
-    }
-
-    def skip(@unused value: => Unit)(implicit pos: CodePositionMaterializer, dummyImplicit: DummyImplicit): Unit = {
-      takeFunIO(cancel, pos.get)
+    def in(value: => F[_, Assertion])(implicit pos: CodePositionMaterializer, d1: DummyImplicit): Unit = {
+      takeBIO(() => value, pos.get)
     }
 
-    def skip(@unused value: => Assertion)(implicit pos: CodePositionMaterializer, dummyImplicit: DummyImplicit, dummyImplicit2: DummyImplicit): Unit = {
+    def skip(@unused value: => Any)(implicit pos: CodePositionMaterializer): Unit = {
       takeFunIO(cancel, pos.get)
     }
 
diff --git a/distage/distage-testkit-scalatest/src/main/scala/org/scalatest/SafeTestReporter.scala b/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/services/scalatest/dstest/SafeTestReporter.scala
similarity index 98%
rename from distage/distage-testkit-scalatest/src/main/scala/org/scalatest/SafeTestReporter.scala
rename to distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/services/scalatest/dstest/SafeTestReporter.scala
index b973ef0e9..389fa6243 100644
--- a/distage/distage-testkit-scalatest/src/main/scala/org/scalatest/SafeTestReporter.scala
+++ b/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/services/scalatest/dstest/SafeTestReporter.scala
@@ -1,4 +1,4 @@
-package org.scalatest
+package izumi.distage.testkit.services.scalatest.dstest
 
 import izumi.distage.testkit.services.dstest.DistageTestRunner.TestStatus.{Done, Running}
 import izumi.distage.testkit.services.dstest.DistageTestRunner.{SuiteData, TestMeta, TestReporter, TestStatus}
diff --git a/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/services/scalatest/dstest/ScalatestWords.scala b/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/services/scalatest/dstest/ScalatestWords.scala
deleted file mode 100644
index 8a52e5149..000000000
--- a/distage/distage-testkit-scalatest/src/main/scala/izumi/distage/testkit/services/scalatest/dstest/ScalatestWords.scala
+++ /dev/null
@@ -1,135 +0,0 @@
-package izumi.distage.testkit.services.scalatest.dstest
-
-import izumi.distage.testkit.services.scalatest.dstest.ScalatestWords.{DSCanVerb, DSMustVerb, DSShouldVerb, DSStringVerbBlockRegistration}
-import org.scalactic.source
-import org.scalactic.source.Position
-
-@org.scalatest.Finders(value = Array("org.scalatest.finders.WordSpecFinder"))
-trait ScalatestWords extends DSShouldVerb with DSMustVerb with DSCanVerb  {
-  protected implicit val subjectRegistrationFunction1: DSStringVerbBlockRegistration = new DSStringVerbBlockRegistration {
-    def apply(left: String, verb: String, pos: source.Position, f: () => Unit): Unit = {
-      registerBranch(left, Some(verb), verb, "apply", 6, -2, pos, f)
-    }
-  }
-
-  def registerBranch(description: String, childPrefix: Option[String], verb: String, methodName: String, stackDepth: Int, adjustment: Int, pos: source.Position, fun: () => Unit): Unit
-}
-
-object ScalatestWords {
-
-  // shameless copypaste from ScalaTest
-
-  abstract class DSStringVerbBlockRegistration {
-    def apply(string: String, verb: String, pos: source.Position, block: () => Unit): Unit
-  }
-
-  abstract class DSStringVerbStringInvocation {
-    def apply(subject: String, verb: String, predicate: String, pos: source.Position): DSResultOfStringPassedToVerb
-  }
-
-  abstract class DSResultOfStringPassedToVerb(val verb: String, val rest: String) {
-    //def taggedAs(firstTestTag: Tag, otherTestTags: Tag*): ResultOfTaggedAsInvocation
-  }
-
-  final class DSResultOfAfterWordApplication(val text: String, val f: () => Unit) {
-    override def toString: String = text
-  }
-
-  abstract class DSSubjectWithAfterWordRegistration {
-    def apply(subject: String, verb: String, resultOfAfterWordApplication: DSResultOfAfterWordApplication, pos: source.Position): Unit
-  }
-
-
-  trait DSShouldVerb {
-
-    trait DSStringShouldWrapperForVerb {
-
-      val leftSideString: String
-
-      val pos: source.Position
-
-      def should(right: String)(implicit svsi: DSStringVerbStringInvocation): DSResultOfStringPassedToVerb = {
-        svsi(leftSideString, "should", right, pos)
-      }
-
-      def should(right: => Unit)(implicit fun: DSStringVerbBlockRegistration): Unit = {
-        fun(leftSideString, "should", pos, right _)
-      }
-
-      def should(resultOfAfterWordApplication: DSResultOfAfterWordApplication)(implicit swawr: DSSubjectWithAfterWordRegistration): Unit = {
-        swawr(leftSideString, "should", resultOfAfterWordApplication, pos)
-      }
-    }
-
-    import scala.language.implicitConversions
-
-    implicit def convertToStringShouldWrapperForVerb(o: String)(implicit position: source.Position): DSStringShouldWrapperForVerb =
-      new DSStringShouldWrapperForVerb {
-        val leftSideString: String = o.trim
-        val pos: Position = position
-      }
-  }
-
-
-  trait DSCanVerb {
-
-    trait DSStringCanWrapperForVerb {
-
-      val leftSideString: String
-
-      val pos: source.Position
-
-      def can(right: String)(implicit svsi: DSStringVerbStringInvocation): DSResultOfStringPassedToVerb = {
-        svsi(leftSideString, "can", right, pos)
-      }
-
-      def can(right: => Unit)(implicit fun: DSStringVerbBlockRegistration): Unit = {
-        fun(leftSideString, "can", pos, right _)
-      }
-
-      def can(resultOfAfterWordApplication: DSResultOfAfterWordApplication)(implicit swawr: DSSubjectWithAfterWordRegistration): Unit = {
-        swawr(leftSideString, "can", resultOfAfterWordApplication, pos)
-      }
-    }
-
-    import scala.language.implicitConversions
-
-    implicit def convertToStringCanWrapper(o: String)(implicit position: source.Position): DSStringCanWrapperForVerb =
-      new DSStringCanWrapperForVerb {
-        val leftSideString: String = o.trim
-        val pos: Position = position
-      }
-  }
-
-  trait DSMustVerb {
-
-    trait DSStringMustWrapperForVerb {
-
-      val leftSideString: String
-
-      val pos: source.Position
-
-      def must(right: String)(implicit svsi: DSStringVerbStringInvocation): DSResultOfStringPassedToVerb = {
-        svsi(leftSideString, "must", right, pos)
-      }
-
-      def must(right: => Unit)(implicit fun: DSStringVerbBlockRegistration): Unit = {
-        fun(leftSideString, "must", pos, right _)
-      }
-
-      def must(resultOfAfterWordApplication: DSResultOfAfterWordApplication)(implicit swawr: DSSubjectWithAfterWordRegistration): Unit = {
-        swawr(leftSideString, "must", resultOfAfterWordApplication, pos)
-      }
-    }
-
-    import scala.language.implicitConversions
-
-    implicit def convertToStringMustWrapperForVerb(o: String)(implicit position: source.Position): DSStringMustWrapperForVerb =
-      new DSStringMustWrapperForVerb {
-        val leftSideString: String = o.trim
-        val pos: Position = position
-      }
-  }
-
-
-}
diff --git a/distage/distage-testkit-scalatest/src/main/scala/org/scalatest/DistageAssertionsMacro.scala b/distage/distage-testkit-scalatest/src/main/scala/org/scalatest/distage/DistageAssertionsMacro.scala
similarity index 90%
rename from distage/distage-testkit-scalatest/src/main/scala/org/scalatest/DistageAssertionsMacro.scala
rename to distage/distage-testkit-scalatest/src/main/scala/org/scalatest/distage/DistageAssertionsMacro.scala
index 71a380f18..b13c324d7 100644
--- a/distage/distage-testkit-scalatest/src/main/scala/org/scalatest/DistageAssertionsMacro.scala
+++ b/distage/distage-testkit-scalatest/src/main/scala/org/scalatest/distage/DistageAssertionsMacro.scala
@@ -1,7 +1,8 @@
-package org.scalatest
+package org.scalatest.distage
 
 import org.scalactic.Prettifier
 import org.scalactic.source.Position
+import org.scalatest.{Assertion, AssertionsMacro}
 
 import scala.reflect.macros.{blackbox, whitebox}
 
diff --git a/distage/distage-testkit-scalatest/src/main/scala/org/scalatest/DistageScalatestReporter.scala b/distage/distage-testkit-scalatest/src/main/scala/org/scalatest/distage/DistageScalatestReporter.scala
similarity index 99%
rename from distage/distage-testkit-scalatest/src/main/scala/org/scalatest/DistageScalatestReporter.scala
rename to distage/distage-testkit-scalatest/src/main/scala/org/scalatest/distage/DistageScalatestReporter.scala
index fa6ff71e5..55c8e8e45 100644
--- a/distage/distage-testkit-scalatest/src/main/scala/org/scalatest/DistageScalatestReporter.scala
+++ b/distage/distage-testkit-scalatest/src/main/scala/org/scalatest/distage/DistageScalatestReporter.scala
@@ -1,4 +1,4 @@
-package org.scalatest
+package org.scalatest.distage
 
 import izumi.distage.framework.model.exceptions.IntegrationCheckException
 import izumi.distage.testkit.services.dstest.DistageTestRunner.{SuiteData, TestMeta, TestReporter, TestStatus}
diff --git a/distage/distage-testkit-scalatest/src/main/scala/org/scalatest/DistageScalatestTestSuiteRunner.scala b/distage/distage-testkit-scalatest/src/main/scala/org/scalatest/distage/DistageScalatestTestSuiteRunner.scala
similarity index 95%
rename from distage/distage-testkit-scalatest/src/main/scala/org/scalatest/DistageScalatestTestSuiteRunner.scala
rename to distage/distage-testkit-scalatest/src/main/scala/org/scalatest/distage/DistageScalatestTestSuiteRunner.scala
index 6f72b0e92..50eb81c6b 100644
--- a/distage/distage-testkit-scalatest/src/main/scala/org/scalatest/DistageScalatestTestSuiteRunner.scala
+++ b/distage/distage-testkit-scalatest/src/main/scala/org/scalatest/distage/DistageScalatestTestSuiteRunner.scala
@@ -1,4 +1,4 @@
-package org.scalatest
+package org.scalatest.distage
 
 import java.util.concurrent.atomic.AtomicBoolean
 
@@ -7,10 +7,11 @@ import io.github.classgraph.ClassGraph
 import izumi.distage.testkit.DebugProperties
 import izumi.distage.testkit.services.dstest.DistageTestRunner._
 import izumi.distage.testkit.services.dstest.{AbstractDistageSpec, DistageTestRunner}
-import izumi.distage.testkit.services.scalatest.dstest.DistageTestsRegistrySingleton
+import izumi.distage.testkit.services.scalatest.dstest.{DistageTestsRegistrySingleton, SafeTestReporter}
 import izumi.distage.testkit.services.scalatest.dstest.DistageTestsRegistrySingleton.SuiteReporter
 import izumi.fundamentals.platform.console.TrivialLogger
 import izumi.fundamentals.platform.functional.Identity
+import org.scalatest._
 import org.scalatest.exceptions.TestCanceledException
 
 import scala.collection.immutable.TreeSet
@@ -66,7 +67,7 @@ object ScalatestInitWorkaround {
 
 }
 
-abstract class DistageScalatestTestSuiteRunner[F[_]](implicit override val tagMonoIO: TagK[F]) extends Suite with AbstractDistageSpec[F] {
+abstract class DistageScalatestTestSuiteRunner[F[_]](implicit override val tagMonoIO: TagK[F]) extends TestSuite with AbstractDistageSpec[F] {
 
   // initialize status early, so that runner can set it to `true` even before this test is discovered
   // by scalatest, if it was already executed by that time
@@ -75,6 +76,7 @@ abstract class DistageScalatestTestSuiteRunner[F[_]](implicit override val tagMo
   override protected final def runNestedSuites(args: Args): Status = throw new UnsupportedOperationException
   override protected final def runTests(testName: Option[String], args: Args): Status = throw new UnsupportedOperationException
   override protected final def runTest(testName: String, args: Args): Status = throw new UnsupportedOperationException
+  override protected def withFixture(test: NoArgTest): Outcome = throw new UnsupportedOperationException
 
   override def run(testName: Option[String], args: Args): Status = {
     DistageTestsRegistrySingleton.registerSuiteReporter(suiteId)(SuiteReporter(args.tracker, args.reporter))
diff --git a/distage/distage-testkit-scalatest/src/main/scala/org/scalatest/TestCancellation.scala b/distage/distage-testkit-scalatest/src/main/scala/org/scalatest/distage/TestCancellation.scala
similarity index 93%
rename from distage/distage-testkit-scalatest/src/main/scala/org/scalatest/TestCancellation.scala
rename to distage/distage-testkit-scalatest/src/main/scala/org/scalatest/distage/TestCancellation.scala
index 2b8e62d07..a8deb9da3 100644
--- a/distage/distage-testkit-scalatest/src/main/scala/org/scalatest/TestCancellation.scala
+++ b/distage/distage-testkit-scalatest/src/main/scala/org/scalatest/distage/TestCancellation.scala
@@ -1,4 +1,4 @@
-package org.scalatest
+package org.scalatest.distage
 
 import org.scalatest.exceptions.TestCanceledException
 
diff --git a/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/DistageTestExample.scala b/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/DistageTestExample.scala
index 5a5a070fc..632131138 100644
--- a/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/DistageTestExample.scala
+++ b/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/DistageTestExample.scala
@@ -115,6 +115,10 @@ abstract class DistageTestExampleBase[F[_]: TagK](implicit F: DIEffect[F]) exten
         F.maybeSuspend(assert(set.size == 3))
     }
 
+    "support tests with no deps" in {
+      F.unit
+    }
+
     "test 1" in {
       service: MockUserRepository[F] =>
         for {
diff --git a/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/IdentityCompatTest.scala b/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/IdentityCompatTest.scala
new file mode 100644
index 000000000..8dbc884df
--- /dev/null
+++ b/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/IdentityCompatTest.scala
@@ -0,0 +1,24 @@
+package izumi.distage.testkit.distagesuite
+
+import izumi.distage.model.effect.DIEffect
+import izumi.distage.testkit.scalatest.DistageSpecScalatest
+import izumi.fundamentals.platform.functional.Identity
+
+final class IdentityCompatTest extends DistageSpecScalatest[Identity] {
+
+  "tests in identity" should {
+
+    "start" in {
+      _: DIEffect[Identity] =>
+        assert(true)
+        println("identitycompat")
+    }
+
+    "skip (should be ignored)" in {
+      _: DIEffect[Identity] =>
+        assume(false)
+    }
+
+  }
+
+}
diff --git a/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/ScalaMockCompatTest.scala b/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/ScalaMockCompatTest.scala
new file mode 100644
index 000000000..7c4a2d2b3
--- /dev/null
+++ b/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/ScalaMockCompatTest.scala
@@ -0,0 +1,30 @@
+package izumi.distage.testkit.distagesuite
+
+import distage.ModuleDef
+import izumi.distage.testkit.TestConfig
+import izumi.distage.testkit.scalatest.DistageSpecScalatest
+import izumi.fundamentals.platform.functional.Identity
+import org.scalamock.scalatest.MockFactory
+import org.scalatest.Assertion
+
+final class ScalaMockCompatTest extends DistageSpecScalatest[Identity] with MockFactory {
+
+  trait TestClass {
+    def method: String
+  }
+
+  override protected def config: TestConfig = super.config.copy(
+    moduleOverrides = new ModuleDef {
+      make[TestClass].from(mock[TestClass])
+    }
+  )
+
+  "mockfactory" should {
+    "be compatible" in {
+      testMock: TestClass =>
+        (testMock.method _).expects().returning("hello")
+        assert(testMock.method == "hello")
+    }
+  }
+
+}
diff --git a/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/ScalatestCompatTest.scala b/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/ScalatestCompatTest.scala
new file mode 100644
index 000000000..34475745e
--- /dev/null
+++ b/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/ScalatestCompatTest.scala
@@ -0,0 +1,32 @@
+package izumi.distage.testkit.distagesuite
+
+import izumi.distage.testkit.scalatest.{AssertIO, DistageSpecScalatest}
+import izumi.fundamentals.platform.functional.Identity
+import org.scalatest.exceptions.TestFailedException
+import org.scalatest.matchers.{must, should}
+
+final class ScalatestCompatTestShould extends DistageSpecScalatest[Identity] with should.Matchers with AssertIO {
+
+  "test" should {
+    "start" in {
+      intercept[TestFailedException](1 should equal(5))
+      1 should equal(1)
+      1 should equal(1)
+      1 should not equal 2
+    }
+  }
+
+}
+
+final class ScalatestCompatTestMust extends DistageSpecScalatest[Identity] with must.Matchers with AssertIO {
+
+  "test" should {
+    "start" in {
+      intercept[TestFailedException](1 must equal(5))
+      1 must equal(1)
+      1 must equal(1)
+      1 must not equal 2
+    }
+  }
+
+}