Keep on moving

あんまりまとまってないことを書きますよ

Property-Based-Testingをkotlin testで学ぶ

こんにちは、Kotlin初心者です。Property-Based-Testingやってみようとおもったけど、あまり良くわかってなかったのでまとめてみました。

TL;DR

  • KotlinでProperty-Based-Testingやってみた
  • kotestでの流れ

サンプル

今回のサンプルはこちらにあります

github.com

Property-Based-Testing

そもそもProperty-Based-Testingとは

www.kzsuzuki.com

こちらのブログによるとこのブログを元ネタに説明してくれています。

jessitron.com

 記事では、Property-based testingの対照概念を、Example-based testingとしています。  Example-based testingとは、無数にある値から何がしかの基準で入力値を選択し、その入力値でコードを動かして、出力値と事後状態を確認する。つまり、普通に行われているテストですよね。

 Property-based testingでは、一つのテストで数百個の入力に対する結果を検証します。  ここで検証するのは、個々の入力に対するそれぞれの出力そのものではなく、(有効な)入力「群」に対する出力「群」のすべてが、statementで規定された性質(property)を満たしていることです。

jqwik作者の説明

Javaでの pbsツール jqwikの作者がpbsについてまとめてくれています。

jqwik.net

hypothesis.works

によるとFuzzingテストとpbsの違いは以下のような感じらしいです。

The main reason I’m drawing it here is that they do feel like they have a different character - property based testing requires you to reason about how your program should behave, while fuzzing can just be applied to arbitrary programs with minimal understanding of their behaviour - and also that fuzzing feels somehow more fundamental. [超訳] プロパティベースのテストでは、プログラムの動作を推論する必要がありますが、ファジングは、動作の理解が最小限の任意のプログラムに適用できます,そして、そのファジングは何かもっと根本的なもののようにに感じます。

ファジングテストはpbsとそんなにちがいはないのかもしれないというのがこの話で出てきています。実際に振る舞いをしっていてテストコードをつくるのが大事らしいですね。 プログラムの動作を推論というのは ^ の話でのstatement(property?)の話とほぼ同義の話をしているようにも思えます。

propertyを、「さまざまなデータポイントにおいて維持されるべき、ハイレベルな振る舞いの仕様」としています。

Property based testing is the construction of tests such that, when these tests are fuzzed, failures in the test reveal problems with the system under test that could not have been revealed by direct fuzzing of that system.

Kotlin testでは振る舞いを知っている上で、Example-based testingを補う概念として説明していますね。

GitHub - kotest/kotest: Powerful, elegant and flexible test framework for Kotlin

The problem is it's very easy to miss errors that occur when edge cases or inputs outside of what you considered are fed in. With property testing, hundreds or thousands of values are fed into the same test, and the values are randomly generated by your property test framework. [超訳] 問題は、エッジケースや想定外の入力が入力された場合に発生するエラーを見逃してしまうことです。プロパティテストでは、何百、何千もの値が同じテストに投入され、その値はプロパティテストフレームワークによってランダムに生成されます。

というわけで、これを使ってテストケースを考えてみます。

Kotlin Test

github.com

Kotlin と Kotlin Test(kotest)を使って実際のコードを書いてみます。

install

gradleでの例です。 Property-based-tesitingを使いたいときは kotestに追加で io.kotest:kotest-property-jvm を追加で入れる必要があります。

dependencies {
    testImplementation("io.kotest:kotest-runner-junit5-jvm:version") // for kotest framework
    testImplementation("io.kotest:kotest-assertions-core-jvm:version") // for kotest core jvm assertions
    testImplementation("io.kotest:kotest-property-jvm:version") // for kotest property test
}

こちらを参考に例を作ってみました。

テストの対象とするメソッドは以下のようなものです。

typealias Adder<T> = (T) -> T

object ListUtil {

    fun <T : Number> T.toAdder(): Adder<T> {
        return when (this) {
            is Long -> {
                { it -> (this as Long + it as Long) as T }
            }
            is Int -> {
                { it -> (this as Int + it as Int) as T }
            }
            is Double -> {
                { it -> (this as Double + it as Double) as T }
            }
            else -> throw AssertionError()
        }
    }

    fun <T : Number> sum(zero: T, list: List<T>): T {
        return list.map { it.toAdder() }.fold(zero) { acc, func -> func(acc) }
    }
}

テストコードは以下のようになります。 個々でのproperty(=「さまざまなデータポイントにおいて維持されるべき、ハイレベルな振る舞いの仕様」)は以下の2点です。 * "空のリストの合計は0" * "空ではないリストの合計はtailの合計にheadを足したもの"

class BusinessLogicTests : FunSpec({
    context("ListUtil.sum") {
        test("空のリストの合計は0") {
            ListUtil.sum(0, emptyList()).shouldBe(0)
        }
        test("空ではないリストの合計はtailの合計にheadを足したもの") {
            forAll<Int, List<Int>> { head, tail ->
                ListUtil.sum(0, listOf(head) + tail) == head + ListUtil.sum(0, tail)
            }
        }
    }
})

上の例では、forAllを使用することで、ランダムに生成されたIntとList[Int]の値を受け取ってテストを行っています。forAllに渡したテストの関数が(2, List()), (4, List(1, 4)のようなランダムな値で順番に呼び出されることでテストが実行されるイメージです。

まとめ

僕の理解だと以下のような流れで追加していくのが良さそうです。 1. サンプルベースのテスト 2. それを補完するためにPropertyをみつけ、Property-Based-Testingをする

propertyを、「さまざまなデータポイントにおいて維持されるべき、ハイレベルな振る舞いの仕様」としています。

もうすこし、複雑な例はまた明日書く予定です。 ビジネスロジックのテストに便利そうなので特定のdataclassを生成するときのTipsをまとめる予定です。

関連

github.com

gakuzzzz.github.io

taketoncheir.hatenablog.com

dev.classmethod.jp

attrsの使いどころとdataclass

masahito.hatenablog.com

この記事は^の記事の続きです。^を読んでから読むと良いと思います。 なお、今回のサンプルコードはこちらにあります。(間違いなどありましたらご指摘ください)

github.com

TL;DR

  • attrsのつかいどころ
    • 今Python3.5 or 3.6を使っている。今後dataclassを使いたい
    • dataclassで slotsなどを使いたい場合にボイラープレートコードを避けたい
  • slotsの効果

attrsとは

www.attrs.org 簡単にいうとPython でclassを書くときのボイラープレートになるところがスッキリかけるものです。 1 Overviewで書かれているコードを軽く解説します。

>>> import attr

>>> @attr.s
... class SomeClass(object):
...     a_number = attr.ib(default=42)
...     list_of_numbers = attr.ib(factory=list)
...
...     def hard_math(self, another_number):
...         return self.a_number + sum(self.list_of_numbers) * another_number


>>> sc = SomeClass(1, [1, 2, 3])
>>> sc
SomeClass(a_number=1, list_of_numbers=[1, 2, 3])

>>> sc.hard_math(3)
19
>>> sc == SomeClass(1, [1, 2, 3])
True
>>> sc != SomeClass(2, [3, 2, 1]) # classレベルでの比較が可能です
True

>>> attr.asdict(sc) # class -> dict 変換
{'a_number': 1, 'list_of_numbers': [1, 2, 3]}

>>> SomeClass() # default値も使えます. factory=list の場合はempty listになります。
SomeClass(a_number=42, list_of_numbers=[])

>>> C = attr.make_class("C", ["a", "b"]) # methodでクラスがつくれちゃいます。
>>> C("foo", "bar")
C(a='foo', b='bar')

attrsこぼれ話

じつはこのライブラリ Python 3.7 で入った dataclass のもとになった実装だったりします。 詳しくはpepを確認してください。 www.python.org

ちなみに dataclass入れるよりattrsをPythonで標準でいれちゃえば? という声もあったみたいですがそれはやめたみたいです。(guidoがstdlibに入れるのに反対している、この辺を参照。)

github.com 「attrsはいいもんだが、これがファイナルアンサーじゃないしいいところだけ取り出そうぜ」という感じっぽい。

attrsのdataclassとの使い分け

attrsを使うべき

  • 今Python3.5 or 3.6を使っている. 2
    • 特に現在namedtupleを使っているが、3.7にupgradeするタイミングでdataclassの使用を考えている
  • dataclassで slotsなどをつかいたい(for Python3.7以降)
@dataclasses.dataclass
class B:
   __slots__ = ('b',) # __slots__ 追加
   b: int

例えばこんな感じでdataclassでも __slots__ が使えることを前回紹介した。が、 __slots__ に設定するところとプロパティを両方書かないといけないため 実行時にエラーになることが結構おおい。

これをattrsをつかうと以下のようにかける

# slots=Trueをつけるとslotsが有効に
@attr.s(auto_attribs=True, slots=True)
class AttrsMessage:
    sender: str
    recipient: str
    body: str

個人的にはdataclassを使いたくなったら無条件でattrsを使うのをおすすめしたいと思っています。 ただプロジェクトの考え方でなるべく使うライブラリを増やしたくない場合はdataclassでも問題はないかもしれません。 が、複数人で開発するときではミスをするばあいが多いため入れておいたほうが便利だと思います。

余談 __slots__ の効用

Pympler を使ってPython3.8での __slots__ を使ったとき使わないときのメモリ使用量を比べてみました。 使い方についてはこちらが詳しいです。 Asizeof モジュールの使い方 | Xoxzoの公式ブログ

# use NamedTuple
Message = NamedTuple("Message", [("sender", str), ("recipient", str), ("body", str)])

if sys.version_info.minor >=7:
    import dataclasses
    @dataclasses.dataclass
    class DataClassMessage:
        sender: str
        recipient: str
        body: str


    @dataclasses.dataclass
    class SlotMessage:
        __slots__ = ["sender", "recipient", "body"]
        sender: str
        recipient: str
        body: str


@attr.s(auto_attribs=True, slots=True, weakref_slot=False)
class AttrsMessage:
    sender: str
    recipient: str
    body: str

if __name__ == "__main__":
    pos = Message(
        sender="sender@exmaple.com",
        recipient="recipient.example.com",
        body="Hello, World!",
    )
    if sys.version_info.minor >=7:
        simple = DataClassMessage(
            sender="sender@exmaple.com",
            recipient="recipient.example.com",
            body="Hello, World!",
        )
        slotted = SlotMessage(
            sender="sender@exmaple.com",
            recipient="recipient.example.com",
            body="Hello, World!",
        )
        print(
            "Dataclass %d, Slotted dataclass %d"
            % asizeof.asizesof(simple, slotted)
        )
    attrs = AttrsMessage(
        sender="sender@exmaple.com",
        recipient="recipient.example.com",
        body="Hello, World!",
    )
Dataclass 536, Slotted dataclass 56
NamedTuple 272,  Attrs 56

こんな感じで3propertiesのclassでも __slots__ のありなしで 10倍程度メモリ使用量が違ったりします。 特に実行回数が多い関数の返り値などでdataclassを使用する場合は使用メモリのためにも使っておくことが無難です。

まとめ

本記事では以下の説明をしました。

  • attrsのつかいどころ
    • 今Python3.5 or 3.6を使っている。今後dataclassを使いたい
    • dataclassで __slots__ などを使いたい場合にボイラープレートコードを避けたい
  • __slots__ の効果

  1. Scalaのcase class, Kotlinのdata class的なものだと思ってもらうと良いと思います(プロパティはデフォルトではimmutableではないですが)

  2. https://devguide.python.org/#status-of-python-branches Python3.5/3.6もまだEOLでない

dataclassと __slots__ について調べた

対象バージョン Python 3.7以降

TL;DR

  • dataclassでも __slots__ を使える
  • __slots__ をつかうとpropertyの追加ができなくなる(自由度が下るが、コードをおいやすくなる)

slots とは

docs.python.org

__slots__ を使うと、(プロパティのように) データメンバを明示的に宣言し、 (明示的に __slots__ で宣言しているか親クラスに存在しているかでない限り) __dict____weakref__ を作成しないようにできます。 __dict__ を使うのに比べて、節約できるメモリ空間はかなり大きいです。 属性探索のスピードもかなり向上できます。

ということらしい。

dataclassの動作

dataclassを自分で使うときは私の用途だと関数の返り値/引数とかで使うときがおおいのでメモリ空間の節約できると便利だと思ってしらべてみました。 また、データ型の表現で使うときはプロパティの追加ができないほうが便利かなーと。

>>> import dataclasses
>>> @dataclasses.dataclass
... class A:
...    b: int
>>> a = A(1)
>>> a.b
1
>>> a.c =2 # propertyを追加できる
>>> a.c
2
>>> dir(a) # __dict__ や __weakref__ が作成される
['__annotations__', '__class__', '__dataclass_fields__', '__dataclass_params__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'b', 'c']

というわけでdataclassでは __slots__ が有効ではないみたい。 dataclassの動作を見てる感じだと、__slots__ を有効にするオプションはないみたい docs.python.org

dataclss with __slots__

www.python.org PEP-557では自動的に追加することは一旦やめる的なことが書かれている。 実際に使うにはこんならしい。

>>> @dataclasses.dataclass
... class B:
...   __slots__ = ('b',) # __slots__ 追加
...   b: int
... 
>>> b = B(2)
>>> dir(b)
['__annotations__', '__class__', '__dataclass_fields__', '__dataclass_params__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'b']
>>> b.d =3 # propetyの追加不可
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'B' object has no attribute 'd'

まとめ

ということで dataclassをつかったクラスでも __slots__ を定義すれば設定できる。ただ、これ何回も書くのめんどいので なんかいも設定するならば attrs 使ったほうがよさそう。明日はattrsを使ったパターンを書こうと思う

Ubuntu 19.10でSony WF-1000XM3 を利用する

Ubunto19.10でSony WF-1000XM3 をつかおうとしたらすぐに使えなかったのでメモを残します。

2020/06/22 追記: Ubuntu20.04でも同様に利用可能なことを確認済み

TL;DR

  • pulseaudio-modules-bt のインストールが必要。公式ではないのでppaなどでいれる。
  • 音量コントロールをイヤホン側でできるようにすると便利

Ubuntu19.10から使えるようになるまで

Ubuntu19.10 ではBluetoothでの接続はできるのですが、なぜか音声の出力で選択できません。というわけで 以下のインストールが必要です。

必要なdebファイルをインストールする。以下のgithub issueに書かれているものを入れること。

github.com

試して見た感じだと pulseaudio-modules-bt がインストールできていれば 無事Ubuntuから使える様子

https://f.bytefuse.net/pulseaudio-modules-bt-ppa/

音量の調整

どうやらUbuntuだけでつかっているとイヤホン本体の音量は調整できない様子。 公式のサポートにならってiOS/Androidのアプリから設定する or L側のタッチセンサー機能を[外音コントロール]から[音量コントロール]へ変更するのがいいみたい。 knowledge.support.sony.jp

私はL側のタッチセンサー機能を[外音コントロール]から[音量コントロール]へ変更するほうにしました。

まとめ

ちょっとひと手間必要でしたが、無事Ubuntu19.10 からつかえるようになりました。 イヤホンを長時間つけ続けるのが苦手なんですが、こちらをつかうことで会議等で長い時間つけ続けるのも楽になりそうです。 ビデオ会議は音声(マイク、スピーカー)で疲れが変わるのでいいものを使う価値は大きいと思います。

先人の皆様に感謝

参考

github.com

https://www.labohyt.net/blog/server/post-4563/

Amazon WorkspacesをUbuntu 19.10で動かす

Amazon WorkSpacesのクライアントをUbuntu 19.10で動かせたのでメモを残します。

TL;DR

  • ArchのAURは探すとなんでもある
  • debファイルのインストール先が探せればうごかせる(ちゃんと動くかは別ですが。。。

Amazon Workspacesとは

Amazon WorkSpaces というマネージド型のデスクトップ (DaaS) ソリューションがあります。 詳しくはこちらを。 aws.amazon.com

公式クライアント

Windows, Macでは公式クライアントが存在します。ありがたいことにLinuxでもうごくとのこと。 しかし、今日現在だとUbuntu 18.04にしか対応してないです。

clients.amazonworkspaces.com

ただ残念ながら最新版の19.10には対応してない。 残念ながらこのぺーじだとaptのレポジトリ経由しかインストール方法が書かれていません。 困ったなーと思ったら友達がおしえてくれました。

ArchのAURレポジトリ

ArchとかAURってなんなのかはここでは省きます。 ちなみにこちらです。

aur.archlinux.org

ここをみると debファイルのダウンロードができます。 ちなみに これをふつうにdebコマンドでインストールできました。

$ sudo dpkg -i workspacesclient_amd64.deb

私はubuntu(gtk)を使ってるのでそのまま動きましたが、gtkを入れてない方はインストールが必要そうです。 ちゃんと試してないですが動かすにはいかが必要そうです。

  • gtk3
  • webkit2gtk
  • libsoup

ひとまず動作するところまでは確認できました。たまにマウスの動きが怪しくなるんですが、いまのところ動いています。

先人に感謝

余談

Archでも以下のような感じでそのまま展開してるだけっぽいです。

package() {
    cd "$srcdir"
    tar axfv data.tar.xz -C "${pkgdir}"
}

作業環境を XPS 13 2-in-1 7390 にした(Windows10/Ubuntu 19.10)

お正月どこも行かなすぎ君です。 お正月に久々に持ち歩いてるノートPCを変えました。Ubnutu19.10でそこそこいい感じで使えています。

TL;DR

  • Linux Desktopでそこそこ便利につかえている
  • 仕事でMacつかっていて便利な辞書とかMagicTouchpadとかは対応してないけど一応なんとかなってる

選び方

想定していた用途はこんな感じ。

  • 持ち歩いて気になることを調べたい。 
  • カフェでコード書きたい
    • PythonJava/Scala(切実) をいい感じで書きたい
    • IntelliJをつかってもそこそこいいDXになってほしい

選定のポイントは - 1.3kg以下 - 英語キーボード - メモリ16GB - Linuxで動かす実績がある - [Optional] 4K (3840x2160)液晶

で、前回も買ったけどnew XPS13にしました。1.27Kgで正月なのでちょっとお安く売ってたこともありこれにしました。

www.dell.com

ひとまず Linuxをいれる

www.eisbahn.jp

買ってからyoichiro さんのブログを見つけてこりゃArch入れるしかないかなーと思ったけど ひとまずUbuntu19.10いれたらそこそこ動いているのでそのまま使っている。 Linuxカーネルのバージョンが 5.3系だからかもしれない。やっぱりダメそうならArchに切り替える予定。 Windowsはちょっとなんかするときにあると便利なので一旦残すことにした。

キーボードの設定

  • CapsLockをCtrlに

こちらを参考にいれてみた

linux.just4fun.biz

MacOSっぽく 右alt でime-on, 左altでime-offがしたくてこちらを設定

qiita.com

辞書

MacOSだと便利な辞書が入っているんだけど、Linux Desktopだとないのでこちらを導入

chrome.google.com

これに英辞郎の辞書をいれた. v144.8のテキストの方は使えた(最新版は暗号化されてるけどいろいろ考えるとしょうがない)

www.eijiro.jp

まだできていないこと

Dell/XPS/XPS-13-7390-2-in-1 - Ubuntu Wiki

ここにも書かれているけどやっぱ以下はうまくいってない.

  • 指紋認証(finger print)
  • suspend/resume
    • 蓋を閉じておいてもそれなりに動いているらしくバッテリーを消費する

あとカメラが使えないはずなのにつかえているっぽい。Google Meetとかでも使えた 実はPyCon mini shizuokaではこのpcから配信したんだけどうまくカメラもつかえていた。 masahito.hatenablog.com

参考

medium.com

まとめ

一部使えない機能があるけどLinux Desktopでそこそこつかえています。 LaptopでLinux Desktopで使いたい人には良いかもです。これで指紋認証とかつかえると最強なんすけどねぇ。 それにしても前にxps 13 9343にUbuntuいれたときはタッチパッドとかが使えるようになるまでに相当設定入れないと動かなかったんですが、 インストールしただけでスルッと動くようになってきましたね。

Dell XPS 13 (9343) - ArchWiki

PyCon mini Shizuoka 2020(online) #python_shizu でスピーカーとして話した

PyCon mini Shizuoka 2020(online) でスピーカーとして話したので感じたことをざっくり書いておきたいと思います。 参加者として

TL;DR

  • オンライン開催楽しかった。Live配信で2トラックで流してくれたおかげで両方話が追えたのがすごい
  • 運営の皆さんお疲れ様でした。
  • スピーカーは話しているときに観衆の反応がわからないのですごい不安になってくる。

オンライン開催について

昨今のコロナウィルスの話に対応してオンライン開催になりました。 決定してから10日くらいで環境を用意したとのことでスタッフの皆さん非常にお疲れ様でした。

shizuoka.pycon.jp

発表内容

仕事でもお世話になっているデータ基盤環境について今回は発表しました。 ここ一年くらいの勉強の成果をまとめた感じです。 マネージドサービスの利用の進めと定型化(template化, framework化)が大事だよってのが伝えたかったことです。

speakerdeck.com

オンラインで話してみた感想

  • スピーカーは話しているときに観衆の反応がわからないのですごい不安になってくる。
    • 思っていた以上に自分が観客の反応をみながら話してたんだなってのが実感できました。
    • 実は 香港 とか 台湾 とか海外で話したとき以上に緊張しました。
    • Twiterで反応があったときはすごい嬉しかった。24motzさんのツイートが話してる側の癒やしでした。

  • 録画をみた感じだと配信がちゃんと成功していてすごい安心感がありました。スタッフの皆さんGJ
  • 緊張するとえーとかまーとかがふえて時間が伸びがちですね。
  • 実はオンライン用に3日くらい前に内容を練り直したので練習不足もたたって時間オーバーしてしまった。練習もっと増やせばよかった。

まとめ

現地に行かずに家から参加するのって初めてで予想がつかなかったんですが参加して本当に良かったなと思います。 実はオンラインで話すかを確認いただいたときにやるかどうか悩んでいたんですが、いまとなってはやってみて本当に良かったと思います。 良いチャレンジの機会になったかなと。なんども書いていますがスタッフのみなさんありがとうございました。

またリアルな場で開催するときには是非参加させてください。