【Scala】ScalaTest ~ with ScalaCheck ~

■ はじめに

https://dk521123.hatenablog.com/entry/2023/03/27/001306
https://dk521123.hatenablog.com/entry/2023/03/28/003906

の続き。

今回は、ScalaTest + ScalaCheck (org.scalatestplus) を使って
プロパティベーステスト (Property Based Testing; PBT)をしてみる

目次

【0】プロパティベーステスト
【1】ScalaCheck
【2】初期設定
【3】サンプル

【0】プロパティベーステスト

* ランダム値を生成し、それに対してテストを行うことで、
 テスト対象コードの満たすべき性質を検証するテスト手法

【1】ScalaCheck

* Scalaのプロパティベーステストツール
 => とりあえず、今は、データ型に応じてテストケースを
  自動で生成してくれるライブラリみたいに捉える

* ScalaCheck 単体でもテストフレームワークとしての機能はある

【2】初期設定

libraryDependencies ++= Seq(
  "org.scalatestplus" %% "scalacheck-1-17" % "3.2.15.0" % "test",
  "org.scalatest" %% "scalatest" % "3.2.15" % "test"
)

https://www.scalatest.org/user_guide/property_based_testing
https://mvnrepository.com/artifact/org.scalatestplus/scalacheck-1-17

【3】サンプル

https://www.scalatest.org/user_guide/generator_driven_property_checks

import org.scalatest.funspec.AnyFunSpec
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks

// テスト対象のクラス
class Fraction(n: Int, d: Int) {
  require(d != 0)
  require(d != Integer.MIN_VALUE)
  require(n != Integer.MIN_VALUE)

  val number = if (d < 0) -1 * n else n
  val denom = d.abs

  override def toString = number + " / " + denom
}

// you must mix in trait org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
//  (previously known as org.scalatest.props.PropertyChecks)
class HelloTest extends AnyFunSpec with ScalaCheckPropertyChecks {
  describe("For Hello World Test") {
    it ("For PBT with ScalaCheck") {
      // forAll :ランダムに値を生成してくれる
      forAll { (n: Int, d: Int) =>
        // wheneverで指定した以外の値でテストしてくれる
        whenever (d != 0 && d != Integer.MIN_VALUE
        && n != Integer.MIN_VALUE) {
          // ★実験:後述「補足:forAll からのテスト値」を参照
          // println(s"n=${n}, d=${d}")
          // テスト対象クラスをNew
          val target = new Fraction(n, d)
          if (n < 0 && d < 0 || n > 0 && d > 0) {
            assert(target.number > 0)
          } else if (n != 0) {
            assert(target.number < 0)
          } else {
            assert(target.number === 0)
          }
          assert(target.denom > 0)
        }
      }
    }
  }
}

補足:forAll からのテスト値

* 出力してみたら、↓みたいな感じで自動生成してテストしてくれる

n=-598548508, d=-1
n=-963303062, d=-831083178
n=-831083178, d=2147483647                                                           
n=2147483647, d=-672235232                                                           
n=0, d=806279986                                                                     
n=806279986, d=-1
n=-1, d=6057460                                                                      
n=6057460, d=1721800716                                                              
n=0, d=-1385432617                                                                   
n=0, d=1

参考文献

https://dev.classmethod.jp/articles/introduction-to-scala-property-based-testing/
https://dev.classmethod.jp/articles/gen-for-localdatetime-by-scalacheck/

関連記事

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
Scala ~ 環境構築編 ~
https://dk521123.hatenablog.com/entry/2023/03/10/193805
Scala ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2023/03/12/184331
Scala ~ 基本編 / 繰り返し ~
https://dk521123.hatenablog.com/entry/2023/01/24/000000
Scala ~ 基本編 / Option型 ~
https://dk521123.hatenablog.com/entry/2023/03/09/000000
Scala ~ 基本編 / メソッド ~
https://dk521123.hatenablog.com/entry/2023/03/03/000000
Scala ~ 基本編 / クラス ~
https://dk521123.hatenablog.com/entry/2023/03/14/000857
Scala ~ 基本編 / コレクション ~
https://dk521123.hatenablog.com/entry/2023/03/13/000345
Scala ~ 基本編 / 日付・日時 ~
https://dk521123.hatenablog.com/entry/2023/03/08/000000
Scala ~ 基本編 / 正規表現
https://dk521123.hatenablog.com/entry/2023/03/18/034704
Scala ~ 基本編 / ジェネリック
https://dk521123.hatenablog.com/entry/2023/03/21/003817
ScalaEnum
https://dk521123.hatenablog.com/entry/2023/01/05/000000
Scala ~ ファイル名・パスの扱い ~
https://dk521123.hatenablog.com/entry/2023/03/11/000000
Scala ~ ファイルハンドリング ~
https://dk521123.hatenablog.com/entry/2023/01/03/000000
ScalaYAML
https://dk521123.hatenablog.com/entry/2023/03/16/012034
ScalaJDBC / DB接続 ~
https://dk521123.hatenablog.com/entry/2023/03/26/000950
ScalaAWS SDK
https://dk521123.hatenablog.com/entry/2023/03/24/211033
SBT ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2023/03/22/000000
SBT ~ 基本編 / build.sbt ~
https://dk521123.hatenablog.com/entry/2023/01/27/000000
SBT ~ 基本編 / sbtコマンド ~
https://dk521123.hatenablog.com/entry/2023/01/26/000000
SBT ~ sbtプラグイン
https://dk521123.hatenablog.com/entry/2023/01/25/000000
JavaでEmail ~ JavaMail / Text ~
https://dk521123.hatenablog.com/entry/2016/07/16/222422
JavaでEmail ~ JavaMail / 添付ファイル ~
https://dk521123.hatenablog.com/entry/2016/07/17/023459
JavaでEmail ~ SMTP認証 ~
https://dk521123.hatenablog.com/entry/2016/11/07/215251
JavaでEmail ~ SMTP認証 / DIGEST-MD5
https://dk521123.hatenablog.com/entry/2016/12/07/222229
JavaでEmail ~ JavaMail / TLS
https://dk521123.hatenablog.com/entry/2017/05/03/163219
JavaでEmail ~ JavaMail / Return-Path・Errors-To ~
https://dk521123.hatenablog.com/entry/2017/05/07/000344
Amazon SES ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2017/04/28/234103
Amazon S3 ~ Boto3編 ~
https://dk521123.hatenablog.com/entry/2019/10/21/230004
Amazon S3 ~ Boto3でファイル存在チェック ~
https://dk521123.hatenablog.com/entry/2022/02/26/182526
AWS Glue ~ Scalaでの実装 ~
https://dk521123.hatenablog.com/entry/2023/03/17/000000
AWS Glue ~ ローカル環境を作成する / Glue v3.0版 ~
https://dk521123.hatenablog.com/entry/2022/01/31/165650
LocalStack ~ ローカルで疑似AWSを作成する ~
https://dk521123.hatenablog.com/entry/2019/12/14/010524
LocalStack ~ ローカルで疑似Lambda/S3/DynamoDBを作成する ~
https://dk521123.hatenablog.com/entry/2019/12/16/231149
SparkからSnowflakeへの接続について考える
https://dk521123.hatenablog.com/entry/2023/03/19/013833
Mockito ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2014/07/18/233904
Mockito ~ 基本編 ~
https://dk521123.hatenablog.com/entry/2014/07/19/121409
EasyMock ~入門編~
https://dk521123.hatenablog.com/entry/2010/01/28/234207

【Scala】ScalaTest ~ 基本編 ~

■ はじめに

https://dk521123.hatenablog.com/entry/2023/03/27/001306

の続き。

今回は、Scalaの単体試験する際に
ScalaTest を使用した時のTipsについてまとめる

目次

【1】初期処理・後処理
 1)BeforeAndAfter
 2)BeforeAndAfterAll
【2】一時的テストを停止する
 1)テストメソッドを停止する
 2)テストクラスを全体を停止する
【3】private メソッドをテストする

【1】初期処理・後処理

1)BeforeAndAfter

* 各テスト前・後に呼び出されるメソッドを定義できる
 => 説明よりサンプルとログの出力結果みた方が分かりやすい

1)サンプル

package  com.example

import org.scalatest.funspec.AnyFunSpec
import org.scalatest.BeforeAndAfter

class HelloWorldTest extends AnyFunSpec with BeforeAndAfter {
  before {
    println("Before") // ★ログ
  }

  after {
    println("After") // ★ログ
  }

  describe("Hello World Test") {
    describe("Normal") {
      it("Set name=Mike") {
        println("Test1") // ☆ログ
        val result = HelloWorld.sayHello("Mike")
        assert(result == "Hello, Mike!!")
      }
    }
    describe("Abnormal") {
      it("should produce IllegalArgumentException when name is empty") {
        assertThrows[IllegalArgumentException] {
          println("Test2") // ☆ログ
          HelloWorld.sayHello("")
        }
      }
    }
  }
}

出力結果

Before
Test1 << 各テスト前に「Before」、後に「After」にCallする
After
Before                                                       
Test2 << 各テスト前に「Before」、後に「After」にCallする                                                        
After

2)BeforeAndAfterAll

* 1テスト前・後に呼び出されるメソッドを定義できる

サンプル

package  com.example

import org.scalatest.funspec.AnyFunSpec
import org.scalatest.BeforeAndAfterAll

class HelloWorldTest extends AnyFunSpec with BeforeAndAfterAll {
  override def beforeAll(): Unit = {
    println("beforeAll")
  }

  override def afterAll(): Unit = {
    println("afterAll")
  }
  // 略(上と同じ)
}

出力結果

beforeAll
Test1
Test2
afterAll

【2】一時的テストを停止する

* Ignore (無視)を指定する

1)テストメソッドを停止する

  describe("Hello World Test") {
    describe("Normal") {
      ignore("Set name=Mike") { // ignore() を指定

出力結果

[info] Hello World Test                                      
[info]   Normal                                              
[info]   - Set name=Mike !!! IGNORED !!!  << ★

2)テストクラスを全体を停止する

import org.scalatest.Ignore

@Ignore // ★ここ
class HelloWorldTest extends AnyFunSpec with BeforeAndAfterAll {

【3】private メソッドをテストする

* 「PrivateMethodTester」「PrivateMethod」

https://www.scalatest.org/user_guide/using_PrivateMethodTester

1)サンプル

import org.scalatest.funspec.AnyFunSpec
import org.scalatest.PrivateMethodTester

class TargetClass {
  // private method
  private def sayHi(name: String): String = {
    require(name.nonEmpty)
    s"Hi, ${name}!!!"
  }
}

class HelloTest extends AnyFunSpec with PrivateMethodTester {
  describe("For Hello World Test") {
    val privateMethod = PrivateMethod[String]('sayHi)
    val targetClass = new TargetClass()
    it ("For private method test") {
      assert(targetClass.invokePrivate(privateMethod("Mike")) == "Hi, Mike!!!")
    }
  }
}

関連記事

ScalaTest ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2023/03/27/001306
ScalaTest ~ with ScalaCheck ~
https://dk521123.hatenablog.com/entry/2023/03/29/000014
ScalaTest ~ with Mockito ~
https://dk521123.hatenablog.com/entry/2023/03/31/002830
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
Scala ~ 基本編 / 繰り返し ~
https://dk521123.hatenablog.com/entry/2023/01/24/000000
Scala ~ 基本編 / Option型 ~
https://dk521123.hatenablog.com/entry/2023/03/09/000000
Scala ~ 基本編 / メソッド ~
https://dk521123.hatenablog.com/entry/2023/03/03/000000
Scala ~ 基本編 / クラス ~
https://dk521123.hatenablog.com/entry/2023/03/14/000857
Scala ~ 基本編 / コレクション ~
https://dk521123.hatenablog.com/entry/2023/03/13/000345
Scala ~ 基本編 / 日付・日時 ~
https://dk521123.hatenablog.com/entry/2023/03/08/000000
Scala ~ 基本編 / 正規表現
https://dk521123.hatenablog.com/entry/2023/03/18/034704
Scala ~ 基本編 / ジェネリック
https://dk521123.hatenablog.com/entry/2023/03/21/003817
ScalaEnum
https://dk521123.hatenablog.com/entry/2023/01/05/000000
Scala ~ ファイル名・パスの扱い ~
https://dk521123.hatenablog.com/entry/2023/03/11/000000
Scala ~ ファイルハンドリング ~
https://dk521123.hatenablog.com/entry/2023/01/03/000000
ScalaYAML
https://dk521123.hatenablog.com/entry/2023/03/16/012034
ScalaJDBC / DB接続 ~
https://dk521123.hatenablog.com/entry/2023/03/26/000950
ScalaAWS SDK
https://dk521123.hatenablog.com/entry/2023/03/24/211033
SBT ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2023/03/22/000000
SBT ~ 基本編 / build.sbt ~
https://dk521123.hatenablog.com/entry/2023/01/27/000000
SBT ~ 基本編 / sbtコマンド ~
https://dk521123.hatenablog.com/entry/2023/01/26/000000
SBT ~ sbtプラグイン
https://dk521123.hatenablog.com/entry/2023/01/25/000000
JavaでEmail ~ JavaMail / Text ~
https://dk521123.hatenablog.com/entry/2016/07/16/222422
JavaでEmail ~ JavaMail / 添付ファイル ~
https://dk521123.hatenablog.com/entry/2016/07/17/023459
JavaでEmail ~ SMTP認証 ~
https://dk521123.hatenablog.com/entry/2016/11/07/215251
JavaでEmail ~ SMTP認証 / DIGEST-MD5
https://dk521123.hatenablog.com/entry/2016/12/07/222229
JavaでEmail ~ JavaMail / TLS
https://dk521123.hatenablog.com/entry/2017/05/03/163219
JavaでEmail ~ JavaMail / Return-Path・Errors-To ~
https://dk521123.hatenablog.com/entry/2017/05/07/000344
Amazon SES ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2017/04/28/234103
Amazon S3 ~ Boto3編 ~
https://dk521123.hatenablog.com/entry/2019/10/21/230004
Amazon S3 ~ Boto3でファイル存在チェック ~
https://dk521123.hatenablog.com/entry/2022/02/26/182526
AWS Glue ~ Scalaでの実装 ~
https://dk521123.hatenablog.com/entry/2023/03/17/000000
AWS Glue ~ ローカル環境を作成する / Glue v3.0版 ~
https://dk521123.hatenablog.com/entry/2022/01/31/165650
LocalStack ~ ローカルで疑似AWSを作成する ~
https://dk521123.hatenablog.com/entry/2019/12/14/010524
LocalStack ~ ローカルで疑似Lambda/S3/DynamoDBを作成する ~
https://dk521123.hatenablog.com/entry/2019/12/16/231149
SparkからSnowflakeへの接続について考える
https://dk521123.hatenablog.com/entry/2023/03/19/013833
Mockito ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2014/07/18/233904
Mockito ~ 基本編 ~
https://dk521123.hatenablog.com/entry/2014/07/19/121409

【Scala】ScalaTest ~ 入門編 ~

■ はじめに

ここ最近、狂ったように、プログラムや調べものをしてきたが
とりあえず、最低限やれることが調べたので、後はブラッシュアップさせていきたい、、、
っと思ったのだが、単体試験のことを忘れていたので、調べてみた。

目次

【0】Scalaのテストフレームワーク
【1】ScalaTest
 1)テストスタイル
 2)別テストフレームワークとの組み合わせ
【2】ScalaTestの導入
【3】Quick Start
 1)テストコード
 2)テスト実行
【4】Hello World

【0】Scalaのテストフレームワーク

* Scalaのテストフレームワークは、以下の通り

[1] ScalaTest << ★これを使ってみる
[2] specs2
[3] ScalaCheck
[4] TestNG

【1】ScalaTest

https://www.scalatest.org/
https://www.scalatest.org/quick_start

1)テストスタイル

* さまざまなテストスタイル(xUnit/BDD(Behavior Driven Developmentなど)
 で書ける
 => 以下の公式ドキュメントを参照(こんなにあると思わなかった)
 => 個人的には、「FunSuite」か「FunSpec」かな、、、

https://www.scalatest.org/user_guide/selecting_a_style

[1] FunSuite

* xUnit形式のスタイル

[2] FunSpec

* RubyのRSpecライクなスタイル

[3] WordSpec

* specs2ライクなスタイル

2)別テストフレームワークとの組み合わせ

* Property-Based Testingとして「ScalaCheck」を使う
* Mock には、Javaのモック「Mockito」を使う
 => Mockito については、以下の関連記事を参照のこと

Mockito ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2014/07/18/233904
Mockito ~ 基本編 ~
https://dk521123.hatenablog.com/entry/2014/07/19/121409

【2】ScalaTestの導入

libraryDependencies ++= Seq(
  "org.scalatest" %% "scalatest" % "3.2.15" % "test"

https://www.scalatest.org/install

【3】Quick Start

https://www.scalatest.org/quick_start

で、テスト実行してみて
テスト環境が整っているかをチェック。

1)テストコード

フォルダ構成

├─src
│  ├─main
│  │  └─scala
│  └─test
│      └─scala
│           └─ HelloTest.scala

HelloTest.scala(The FlatSpec style)

import collection.mutable.Stack
import org.scalatest._
import flatspec._
import matchers._

class HelloTest extends AnyFlatSpec with should.Matchers {
  "A Stack" should "pop values in last-in-first-out order" in {
    val stack = new Stack[Int]
    stack.push(1)
    stack.push(2)
    stack.pop() should be (2)
    stack.pop() should be (1)
  }

  it should "throw NoSuchElementException if an empty stack is popped" in {
    val emptyStack = new Stack[Int]
    a [NoSuchElementException] should be thrownBy {
      emptyStack.pop()
    } 
  }
}

2)テスト実行

sbt test

[info] HelloTest:
[info] A Stack                                                                       
[info] - should pop values in last-in-first-out order                                
[info] - should throw NoSuchElementException if an empty stack is popped             
[info] Run completed in 742 milliseconds.
[info] Total number of tests run: 2                                                  
[info] Suites: completed 1, aborted 0                                                
[info] Tests: succeeded 2, failed 0, canceled 0, ignored 0, pending 0                
[info] All tests passed.

【4】Hello World

* 自分の簡単なコードでテストしてみる

1)サンプル

HelloWorld.scala

package  com.example

object HelloWorld {
  def sayHello(name: String): String = {
    require(name.nonEmpty)
    s"Hello, ${name}."
  }
}

HelloWorldTest.scala

package  com.example

import org.scalatest.funspec.AnyFunSpec

class HelloWorldTest extends AnyFunSpec {
  describe("Hello World Test") {
    describe("Normal") {
      it("Set name=Mike") {
        val result = HelloWorld.sayHello("Mike")
        assert(result == "Hello, Mike!!")
      }
    }
    describe("Abnormal") {
      it("should produce IllegalArgumentException when name is empty") {
        assertThrows[IllegalArgumentException] {
         HelloWorld.sayHello("")
        }
      }
    }
  }
}

テスト実行

sbt test

[info] HelloWorldTest:                                                               
[info] Hello World Test                                                              
[info]   Normal                                                                      
[info]   - should produce NoSuchElementException when head is invoked                
[info]   - Set name=Mike                                                             
[info]   Abnormal                                                                    
[info]   - should produce IllegalArgumentException when name is empty                
[info] Run completed in 740 milliseconds.
[info] Total number of tests run: 2
[info] Suites: completed 2, aborted 0
[info] Tests: succeeded 2, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.

関連記事

ScalaTest ~ 基本編 ~
https://dk521123.hatenablog.com/entry/2023/03/28/003906
ScalaTest ~ with ScalaCheck ~
https://dk521123.hatenablog.com/entry/2023/03/29/000014
ScalaTest ~ with Mockito ~
https://dk521123.hatenablog.com/entry/2023/03/31/002830
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
Scala ~ 基本編 / 繰り返し ~
https://dk521123.hatenablog.com/entry/2023/01/24/000000
Scala ~ 基本編 / Option型 ~
https://dk521123.hatenablog.com/entry/2023/03/09/000000
Scala ~ 基本編 / メソッド ~
https://dk521123.hatenablog.com/entry/2023/03/03/000000
Scala ~ 基本編 / クラス ~
https://dk521123.hatenablog.com/entry/2023/03/14/000857
Scala ~ 基本編 / コレクション ~
https://dk521123.hatenablog.com/entry/2023/03/13/000345
Scala ~ 基本編 / 日付・日時 ~
https://dk521123.hatenablog.com/entry/2023/03/08/000000
Scala ~ 基本編 / 正規表現
https://dk521123.hatenablog.com/entry/2023/03/18/034704
Scala ~ 基本編 / ジェネリック
https://dk521123.hatenablog.com/entry/2023/03/21/003817
ScalaEnum
https://dk521123.hatenablog.com/entry/2023/01/05/000000
Scala ~ ファイル名・パスの扱い ~
https://dk521123.hatenablog.com/entry/2023/03/11/000000
Scala ~ ファイルハンドリング ~
https://dk521123.hatenablog.com/entry/2023/01/03/000000
ScalaYAML
https://dk521123.hatenablog.com/entry/2023/03/16/012034
ScalaJDBC / DB接続 ~
https://dk521123.hatenablog.com/entry/2023/03/26/000950
ScalaAWS SDK
https://dk521123.hatenablog.com/entry/2023/03/24/211033
SBT ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2023/03/22/000000
SBT ~ 基本編 / build.sbt ~
https://dk521123.hatenablog.com/entry/2023/01/27/000000
SBT ~ 基本編 / sbtコマンド ~
https://dk521123.hatenablog.com/entry/2023/01/26/000000
SBT ~ sbtプラグイン
https://dk521123.hatenablog.com/entry/2023/01/25/000000
JavaでEmail ~ JavaMail / Text ~
https://dk521123.hatenablog.com/entry/2016/07/16/222422
JavaでEmail ~ JavaMail / 添付ファイル ~
https://dk521123.hatenablog.com/entry/2016/07/17/023459
JavaでEmail ~ SMTP認証 ~
https://dk521123.hatenablog.com/entry/2016/11/07/215251
JavaでEmail ~ SMTP認証 / DIGEST-MD5
https://dk521123.hatenablog.com/entry/2016/12/07/222229
JavaでEmail ~ JavaMail / TLS
https://dk521123.hatenablog.com/entry/2017/05/03/163219
JavaでEmail ~ JavaMail / Return-Path・Errors-To ~
https://dk521123.hatenablog.com/entry/2017/05/07/000344
Amazon SES ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2017/04/28/234103
Amazon S3 ~ Boto3編 ~
https://dk521123.hatenablog.com/entry/2019/10/21/230004
Amazon S3 ~ Boto3でファイル存在チェック ~
https://dk521123.hatenablog.com/entry/2022/02/26/182526
AWS Glue ~ Scalaでの実装 ~
https://dk521123.hatenablog.com/entry/2023/03/17/000000
AWS Glue ~ ローカル環境を作成する / Glue v3.0版 ~
https://dk521123.hatenablog.com/entry/2022/01/31/165650
LocalStack ~ ローカルで疑似AWSを作成する ~
https://dk521123.hatenablog.com/entry/2019/12/14/010524
LocalStack ~ ローカルで疑似Lambda/S3/DynamoDBを作成する ~
https://dk521123.hatenablog.com/entry/2019/12/16/231149
SparkからSnowflakeへの接続について考える
https://dk521123.hatenablog.com/entry/2023/03/19/013833
Mockito ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2014/07/18/233904
Mockito ~ 基本編 ~
https://dk521123.hatenablog.com/entry/2014/07/19/121409