Skip to content

Commit

Permalink
Test opinions (#2)
Browse files Browse the repository at this point in the history
Add a test-focused library
  • Loading branch information
alterationx10 authored Jul 7, 2023
1 parent 0d80e23 commit 5e97859
Show file tree
Hide file tree
Showing 13 changed files with 267 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pr-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ jobs:
java-version: "17"
cache: "sbt"
- name: Test Opinions
run: sbt opinions/test
run: sbt test

25 changes: 20 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,28 @@ Scala 3 (and only Scala 3).

## Installation

Check the badge above, or the latest GitHub release for the latest version.
Check the badge above, or the latest GitHub release for the latest version
to replace `x.y.z`.

### sbt

```scala
libraryDependencies += "com.alterationx10" %% "opinionated-zio" % "0.0.1"
libraryDependencies += "com.alterationx10" %% "opinionated-zio" % "x.y.z"
libraryDependencies += "com.alterationx10" %% "opinionated-zio-test" % "x.y.z" % Test
```

### scala cli

```scala
//> using lib com.alterationx10::opinionated-zio:v0.0.1
//> using dep com.alterationx10::opinionated-zio:x.y.z
//> using test.dep com.alterationx10::opinionated-zio-test:x.y.z
```

### mill

```scala
ivy"com.alterationx10::opinionated-zio:v0.0.1"
ivy"com.alterationx10::opinionated-zio:x.y.z"
ivy"com.alterationx10::opinionated-zio-test:x.y.z"
```

## Example Usages
Expand Down Expand Up @@ -90,4 +94,15 @@ val superLayer: ZLayer[Int & Config, Nothing, Service] =
AutoLayer.as[Service, ServiceImpl]


```
```

## Example Test Library Usages

Everything is bundled into one package for the test library as well, and to use
it, you only need to

```scala
import test.opinons.*
```

More docs to come, but please check the corresponding tests for examples.
10 changes: 9 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,19 @@ lazy val root = (project in file("."))
.settings(
publish / skip := true
)
.aggregate(opinions)
.aggregate(opinions, testOpinions)

lazy val opinions = (project in file("opinions"))
.settings(
name := "opinionated-zio",
libraryDependencies ++= Dependencies.opinions,
testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework")
)

lazy val testOpinions = (project in file("test-opinions"))
.settings(
name := "opinionated-zio-test",
libraryDependencies ++= Dependencies.testOpinions,
testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework")
)
.dependsOn(opinions)
4 changes: 4 additions & 0 deletions opinions/src/main/scala/opinions/ZIO.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ extension [Z: Tag](z: Z)
*/
def uio: UIO[Z] = ZIO.succeed(z)

/** Wraps an instance z: Z in ZIO.fail
*/
def fail: IO[Z, Nothing] = ZIO.fail(z)

extension [R: Tag, E: Tag, A: Tag](zio: ZIO[R, E, A])
/** Wraps a zio: ZIO[R, E, A] as ZLayer(zio)
*/
Expand Down
7 changes: 7 additions & 0 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,11 @@ object Dependencies {
"dev.zio" %% "zio-config-typesafe" % Versions.zioConfig
)

val testOpinions: Seq[ModuleID] = Seq(
"dev.zio" %% "zio-test" % Versions.zio,
"dev.zio" %% "zio-test-sbt" % Versions.zio,
"dev.zio" %% "zio-test-magnolia" % Versions.zio,
"dev.zio" %% "zio-mock" % Versions.zioMock
)

}
15 changes: 15 additions & 0 deletions test-opinions/src/main/scala/test/opinions/Assertions.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package test.opinions

import zio.*
import zio.test.*
import opinions.*

extension [A](a: A)

/** Wrap a: A in Assertion.equalTo(a)
*/
def eqTo: Assertion[A] = Assertion.equalTo(a)

/** Wrap a: A in Assertion.not(Assertion.equalTo(a))
*/
def neqTo: Assertion[A] = Assertion.not(a.eqTo)
14 changes: 14 additions & 0 deletions test-opinions/src/main/scala/test/opinions/Expectations.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package test.opinions

import zio.*
import zio.mock.*

extension [A: Tag](a: A)

/** Wrap a: A in Expectation.value(a)
*/
def expected: Result[Any, Nothing, A] = Expectation.value(a)

/** Wrap a: A in Expectation.failure(a)
*/
def expectedF: Result[Any, A, Nothing] = Expectation.failure(a)
24 changes: 24 additions & 0 deletions test-opinions/src/main/scala/test/opinions/Gens.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package test.opinions

import zio.test.*
import zio.*

object GenRandom:

/** Generate a random List[A]
* @param count
* The number of elements to generate
* @param gen
* The implicit Gen[Any, A] to use.
* @tparam A
*/
def apply[A](count: Int)(using gen: Gen[Any, A]): UIO[List[A]] =
gen.runCollectN(count)

/** Generate a random A
* @param gen
* The implicit Gen[Any, A] to use.
* @tparam A
*/
def apply[A](using gen: Gen[Any, A]): UIO[A] =
GenRandom[A](1).map(_.head)
37 changes: 37 additions & 0 deletions test-opinions/src/main/scala/test/opinions/Mocks.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package test.opinions

import zio.*
import zio.mock.*
import zio.test.Assertion

extension [S, E: Tag, I, O: Tag](serviceMethod: Mock[S]#Effect[I, E, O])

/** For a given ZIO Mock Capability (serviceMethod), apply the given values as
* Expectation.value/Assertion.equalTo
* @param o
* Expected output value
* @param i
* Asserted input value
*/
def expectWhen(o: O, i: I): Expectation[S] =
serviceMethod.apply(i.eqTo, o.expected)

/** For a given ZIO Mock Capability (serviceMethod), apply the given values as
* Expectation.failure/Assertion.equalTo
* @param e
* Expected output failure value
* @param i
* Asserted input value
*/
def expectWhenF(e: E, i: I): Expectation[S] =
serviceMethod.apply(i.eqTo, e.expectedF)

/** For a given ZIO Mock Capability (serviceMethod), apply the given
* Expectation Result/Assertion
* @param o
* The expected output Result/Expectation
* @param i
* The expected input assertion
*/
def expectWhen(o: Result[Any, E, O], i: Assertion[I]): Expectation[S] =
serviceMethod.apply(i, o)
16 changes: 16 additions & 0 deletions test-opinions/src/test/scala/test/opinions/AssertionsSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package test.opinions

import zio.*
import zio.test.*
import opinions.*

object AssertionsSpec extends ZIOSpecDefault:
override def spec: Spec[TestEnvironment with Scope, Any] =
suite("AssertionsSpec")(
test("eqTo") {
assertZIO(42.uio)(42.eqTo)
},
test("neqTo") {
assertZIO(42.uio)(69.neqTo)
}
)
17 changes: 17 additions & 0 deletions test-opinions/src/test/scala/test/opinions/ExpectationsSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package test.opinions

import zio.*
import zio.test.*
import zio.mock.*

object ExpectationsSpec extends ZIOSpecDefault:

override def spec: Spec[TestEnvironment with Scope, Any] =
suite("ExpectationsSpec")(
test("expected") {
assertZIO(42.expected.io(()))(42.eqTo)
},
test("expectedF") {
assertZIO(42.expectedF.io(()).flip)(42.eqTo)
}
)
32 changes: 32 additions & 0 deletions test-opinions/src/test/scala/test/opinions/GensSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package test.opinions

import zio.*
import zio.test.*
import opinions.*
import zio.test.magnolia.DeriveGen

import java.time.Instant
import java.util.UUID

object GensSpec extends ZIOSpecDefault:

case class SomeModel(a: Int, b: String, c: Instant, d: UUID)
given Gen[Any, SomeModel] = DeriveGen[SomeModel]

override def spec: Spec[TestEnvironment with Scope, Any] =
suite("GensSpec")(
test("Generate a random List of case class instances") {
for {
someNumber <- Random.nextIntBetween(10, 50)
models <- GenRandom[SomeModel](someNumber)
} yield assertTrue(
models.length == someNumber,
models.distinct.length == someNumber
)
},
test("Generate a random instance of a case class") {
for {
model <- GenRandom[SomeModel]
} yield assertCompletes
}
)
71 changes: 71 additions & 0 deletions test-opinions/src/test/scala/test/opinions/MocksSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package test.opinions

import zio.*
import zio.test.*
import zio.mock.*
import opinions.*

object MocksSpec extends ZIOSpecDefault:

trait SomeService:
def get(id: Int): Task[String]

object SomeMockService extends Mock[SomeService]:
object Get extends Effect[Int, Throwable, String]

val compose: URLayer[Proxy, SomeService] =
ZLayer {
for {
proxy <- ZIO.service[Proxy]
} yield new SomeService:
override def get(id: RuntimeFlags): Task[String] = proxy(Get, id)
}
override def spec: Spec[TestEnvironment with Scope, Any] =
suite("MocksSpec + ")(
test("expectWhen of values") {
for {
result <- ZIO
.serviceWithZIO[SomeService](_.get(42))
.provide(SomeMockService.Get.expectWhen("forty two", 42))
_ <- ZIO
.serviceWithZIO[SomeService](_.get(42))
.provide(
// Comparison without expectWhen extension method
SomeMockService
.Get(Assertion.equalTo(42), Expectation.value("forty two"))
)
} yield assertTrue(result == "forty two")
},
test("expectWhenF of values") {
for {
error <-
ZIO
.serviceWithZIO[SomeService](_.get(42))
.flip
.provide(
SomeMockService.Get.expectWhenF(new Exception("boom"), 42)
)
} yield assertTrue(error.getMessage == "boom")
},
test("expectWhen of assertions") {
for {
result <-
ZIO
.serviceWithZIO[SomeService](_.get(42))
.provide(
SomeMockService.Get.expectWhen("forty two".expected, 42.eqTo)
)
error <-
ZIO
.serviceWithZIO[SomeService](_.get(42))
.flip
.provide(
SomeMockService.Get
.expectWhen(new Exception("boom").expectedF, 42.eqTo)
)
} yield assertTrue(
result == "forty two",
error.getMessage == "boom"
)
}
)

0 comments on commit 5e97859

Please sign in to comment.