Halo, Habr! Tidaklah cukup menulis kode yang baik. Kami masih perlu menutupinya dengan Tes Unit yang baik. Di artikel terakhir, saya membuat web server sederhana. Sekarang saya akan mencoba menulis berapa banyak tes. Reguler, Berbasis properti, dan diejek. Untuk detailnya, selamat datang di bawah cat.
Kandungan
- Scala Belajar: Bagian 1 - Game Ular
- Skala Pembelajaran: Bagian 2 - Lembar Todo dengan Unggahan Gambar
- Skala Pembelajaran: Bagian 3 - Tes Unit
Tautan
Sumber
Gambar gambar buruh pelabuhan
Dan untuk pengujian unit Anda membutuhkan 3 libs.
- Perpustakaan untuk membuat tes
- Library yang akan menghasilkan data uji
- Perpustakaan yang akan mengejek objek
Saya menggunakan perpustakaan ScalaTest untuk membuat tes
"org.scalatest" %% "scalatest" % "3.2.0" % Test
Saya menggunakan ScalaCheck untuk menghasilkan data pengujian untuk pengujian berbasis Properti .
"org.scalacheck" %% "scalacheck" % "1.14.3" % Test
dan ekstensi yang menggabungkan ScalaTest + ScalaCheck ScalaTestPlusScalaCheck
"org.scalatestplus" %% "scalacheck-1-14" % "3.2.0.0" % Test
Saya menggunakan ScalaMock untuk mengejek objek
"org.scalamock" %% "scalamock" % "4.4.0" % Test
Kelas sederhana yang mewakili jenis string yang diisi (tidak kosong). Kami sekarang akan mengujinya.
package domain.common
sealed abstract case class FilledStr private(value: String) {
def copy(): FilledStr = new FilledStr(this.value) {}
}
object FilledStr {
def apply(value: String): Option[FilledStr] = {
val trimmed = value.trim
if (trimmed.nonEmpty) {
Some(new FilledStr(trimmed) {})
} else {
None
}
}
}
Membuat kelas untuk pengujian kami
class FilledStrTests extends AnyFlatSpec with should.Matchers with ScalaCheckPropertyChecks {
}
Kami membuat metode yang akan memeriksa bahwa saat membuat kelas kami dari baris yang sama, kami akan menerima data yang sama.
"equals" should "return true fro equal value" in {
val str = "1234AB"
val a = FilledStr(str).get
val b = FilledStr(str).get
b.equals(a) should be(true)
}
Dalam pengujian terakhir, kami menyembunyikan kode menjadi string yang dibuat secara manual. Sekarang mari kita lakukan pengujian menggunakan data yang dihasilkan. Kita akan menggunakan pendekatan berbasis properti di mana properti fungsi diuji, sehingga dengan data masukan seperti itu, kita akan menerima data keluaran tersebut.
"constructor" should "save expected value" in {
forAll { s: String =>
// . .
whenever(s.trim.nonEmpty) {
val a = FilledStr(s).get
a.value should be(s)
}
}
}
Anda dapat secara eksplisit mengonfigurasi generator data pengujian untuk hanya menggunakan data yang kami butuhkan. Contohnya seperti ini:
//
val evenInts = for (n <- Gen.choose(-1000, 1000)) yield 2 * n
//
forAll (evenInts) { (n) => n % 2 should equal (0) }
Anda juga tidak dapat meneruskan generator kami secara eksplisit, tetapi menentukan implikasinya melalui Arbitrary sehingga secara otomatis diteruskan sebagai generator untuk diuji. Contohnya seperti ini:
implicit lazy val myCharArbitrary = Arbitrary(Gen.oneOf('A', 'E', 'I', 'O', 'U'))
val validChars: Seq[Char] = List('X')
// Arbitrary[Char] .
forAll { c: Char => validChars.contains(c) }
Objek kompleks juga dapat dibuat menggunakan Arbitrary.
case class Foo(intValue: Int, charValue: Char)
val fooGen = for {
intValue <- Gen.posNum[Int]
charValue <- Gen.alphaChar
} yield Foo(intValue, charValue)
implicit lazy val myFooArbitrary = Arbitrary(fooGen)
forAll { foo: Foo => (foo.intValue < 0) == && !foo.charValue.isDigit }
Sekarang mari kita coba menulis ujian dengan lebih serius. Kami akan meniru dependensi untuk TodosService. Ini Menggunakan 2 repositori dan repositori pada gilirannya menggunakan abstraksi atas transaksi UnitOfWork. Mari kita uji metode paling sederhana.
def getAll(): F[List[Todo]] =
repo.getAll().commit()
Yang hanya memanggil repositori, memulai transaksi di dalamnya untuk membaca daftar Todo, mengakhirinya dan mengembalikan hasilnya. Juga dalam pengujian, alih-alih F [_], monad Id diletakkan, yang hanya mengembalikan nilai yang disimpan di dalamnya.
class TodoServiceTests extends AnyFlatSpec with MockFactory with should.Matchers {
"geAll" should " " in {
// .
implicit val tr = mock[TodosRepositoryContract[Id, Id]]
implicit val ir = mock[InstantsRepositoryContract[Id]]
implicit val uow = mock[UnitOfWorkContract[Id, List[Todo], Id]]
// . implicit
val service= new TodosService[Id, Id]()
// Id Todo
val list: Id[List[Todo]] = List(Todo(1, "2", 3, Instant.now()))
// getAll uow 1
(tr.getAll _).expects().returning(uow).once()
// commit 1
(uow.commit _).expects().returning(list).once()
// getAll
//
service.getAll() should be(list)
}
}
Tes menulis di Scala ternyata sangat menyenangkan, dan ScalaCheck, ScalaTest, ScalaMock ternyata adalah perpustakaan yang sangat bagus. Serta perpustakaan untuk membuat tapir API dan perpustakaan untuk server http4s dan perpustakaan untuk aliran fs2. Sejauh ini, lingkungan dan perpustakaan Scala hanya menimbulkan emosi positif dalam diri saya. Saya berharap tren ini akan terus berlanjut.