【Scala】ScalaTest ~ ScalaMock ~

■ はじめに

https://dk521123.hatenablog.com/entry/2023/03/31/002830

で、Java由来の Mockito を触ったが
今回は、ScalaTest + ScalaMock を触ってみる。

目次

【1】ScalaMock
【2】インストール
【3】使用上の注意
 1)final / private メソッド・クラスはサポートされていない
【4】サンプル
 例1:Hello world
 例2:スタブ

【1】ScalaMock

* PureのScala製のMockツール
* コンパイラプラグインでモックのソースコードを
 自動生成するという手法を採用
 => シングルトーンや final クラスのモックも作成可能

https://scalamock.org/
https://github.com/paulbutcher/ScalaMock

【2】インストール

https://scalamock.org/quick-start/
https://scalamock.org/user-guide/installation/

libraryDependencies ++= Seq(
  // ★注目
  "org.scalamock" %% "scalamock" % "5.1.0" % Test,
  "org.scalatest" %% "scalatest" % "3.2.18" % Test
)

【3】使用上の注意

1)final / private メソッド・クラスはサポートされていない

https://scalamock.org/user-guide/faq/

Can I mock final / private methods or classes?

This is not supported, as mocks generated with macros are implemented
 as subclasses of the type to mock. 
So private and final methods cannot be overridden.
You may want to try using an adapter or façade in your code
 to make it testable.
It is better to test against a trait/interface instead of
 a concrete implementation. 
...
There are libraries that support this kind of mocking, such as PowerMock. 

自分用メモ

LocalDate/LocalDateTime/System.currentTimeMillis() など を mock しようとしたが、
これらは、final なので、これじゃー使えない、、、

【4】サンプル

https://scalamock.org/quick-start/
Greetings.scala

object Greetings {
  trait Formatter {
    def format(s: String): String
  }

  object EnglishFormatter extends Formatter {
    def format(s: String): String = s"Hello $s!!"
  }

  def sayHello(name: String, formatter: Formatter): Unit = {
    println(formatter.format(name))
  }
}

例1:Hello world

GreetingsTest.scala

import org.scalatest.matchers.should
import org.scalamock.scalatest.MockFactory

class GreetingsTest extends AnyFunSuite with should.Matchers with MockFactory {
  test("Hello World Test") {
    val mockFormatter = mock[Formatter]
    (mockFormatter.format _).expects("Mike").returning("Hello Mike!!").once()
    Greetings.sayHello("Mike", mockFormatter)
  }
}

例2:スタブ

import Greetings.Formatter
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should
import org.scalamock.scalatest.MockFactory

class GreetingsTest extends AnyFunSuite with should.Matchers with MockFactory {
  test("Hello World Test") {
    // ★スタブ
    val mockFormatter = stub[Formatter]
    val bond = "Mr Bond"

    (mockFormatter.format _).when(bond).returns("Ah, Mr Bond. I've been expecting you")
    Greetings.sayHello(bond, mockFormatter)
    (mockFormatter.format _).verify(bond).once()
  }
}

関連記事

Scala ~ テスティングフレームワーク
https://dk521123.hatenablog.com/entry/2024/06/07/183708
ScalaTest ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2023/03/27/001306
ScalaTest ~ 基本編 ~
https://dk521123.hatenablog.com/entry/2023/03/28/003906
ScalaTest ~ with Mockito ~
https://dk521123.hatenablog.com/entry/2023/03/31/002830
ScalaTest ~ with ScalaCheck ~
https://dk521123.hatenablog.com/entry/2023/03/29/000014
ScalaTest ~ with Coverage ~
https://dk521123.hatenablog.com/entry/2023/08/07/222945
Scala ~ 環境構築編 ~
https://dk521123.hatenablog.com/entry/2023/03/10/193805
Scala ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2023/03/12/184331
PowerMock ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2018/04/09/224904