数学文章作法 基礎編を読んだ
『数学文章作法 基礎編』本を読んで良かったので、勢いで書いてます。
この本は何か
分かりやすい文章の書き方を解説した本です。対象読者は解説するような文章を書く人で、特に論文のような文章を書く人に向けた本です。著者は結城 浩(ゆうき ひろし)さん。
インタビュー記事があったのでなんとなく貼っておきます。
どんな人におすすめか
文章を書く人におすすめです。内容は数式を書くような文章で解説されていますが、読者に何かを教える本を書こうとしている人全員におすすめです。この本自体も解説されている方法で書かれているので分かりやすいです。解説と実例が同時に体験できるので内容に納得せざるを得ません。
また、自分はこういった文章を書く中で「なんか読みづらいなぁ……」と感じていたので読み始めたのですが、読んでいると文章を書く側だけでなく技術書を読む人にとってもいいかもと思いました。というのは、文章を読んでいると理解できないときがありますが、この本の分かりづらい文章に当てはまれば、なぜ理解できないかが客観的に判断できるからです。
あとは仕事をしている人にもおすすめです。どう話せば相手が理解しやすいかということに応用できそうだからです。自分は、考えを相手にまとめて話すことが上手くないので、この本に書いてあることを実践すればマシになりそうだなと思いました。
つまり人と関わる人みんなにおすすめです。
内容について
文章を書くときに気をつけることや、どうすれば分かりやすい文章になるか、数式はどう書くか、例の作り方、問題の作り方、目次や索引など文章を書くために必要なことが全て書いてあります。
4章の数式と命題は読んでいるときあんまり関係ないかなと思っていましたが、今振り返るとコードを載せるときに参考になりそうです。
どの章も良かったですが、とくに5章の例
や7章の目次と索引
は勉強になりました。読者が理解しやすい例や索引の作り方はどうやって作っているのか分からなかったのでためになります(索引を作るような文章はめったに書きませんが)。
おわりに
大学生になったときに、『理科系の作文技術』という本が入学前に読む課題であったのですが、あの本も良かった記憶があります。『数学文章作法 基礎編』と比べると堅い印象がありますが、『理科系の作文技術』を読んでなかったら自分の文章は今よりもひどいものだったでしょう。内容はほとんど忘れてしまったので、また機会があれば読みたいです(読まない)。ちなみに『数学文章作法 基礎編』の参考文献にも含まれていました。
今後は、『数学文章作法 基礎編』の内容を活かして記事を書いたり、同人誌書いたりしたいと思います。
Angular の HttpClient を使うサービスクラスのテスト
最近、TypeScript の楽しさに気づきました。
HttpTestingModule
と HttpTestingController
を使ったテストの書き方です。
テスト対象を用意するために https://angular.jp/tutorial/toh-pt6 で公開されているプロジェクトを使います。
このプロジェクトの HeroService
をテストします。
概要
HttpClientTestingModule
と HttpClientTestingModule
を使えば、テスト対象のクラスの動きだけでなく、リクエストした内容が期待通りの結果になっているか検証できる。
公式が詳しいのでもっと知りたい人は、公式ドキュメントの HTTPリクエストのテスト を参照しよう。
準備
これ を落としてきて、任意の場所で展開します。
動作することを確認し、テストも動かして全て通ることを確認します。
hero.service.spec.ts
を作成し、以下のように書きます。
import { MessageService } from "./message.service"; import { HttpClient } from '@angular/common/http'; import { HeroService } from './hero.service'; describe('HeroService', () => { let httpClientSpy = jasmine.createSpyObj<HttpClient>(["get"]); let messageServiceSpy = jasmine.createSpyObj<MessageService>(["add"]); let service: HeroService; beforeEach(() => { service = new HeroService(httpClientSpy, messageServiceSpy) }); it('should be created', () => { expect(service).toBeTruthy(); }); });
テストが通っているか確認して、問題なければ次に進みましょう。
ちなみにここでやっているのは、サービスに必要な2つのインスタンス(httpClientSpy
, messageServiceSpy
)のスパイオブジェクトを作成し、そのスパイオブジェクトを引数にサービスを生成しています。
TetBed を使った方法に変える
引数で渡すのは面倒なので、TestBed
を使って生成するように変更します。
import { MessageService } from "./message.service"; import { HttpClient } from '@angular/common/http'; import { HeroService } from './hero.service'; import { TestBed } from '@angular/core/testing'; describe('HeroService', () => { let httpClientSpy = jasmine.createSpyObj<HttpClient>(["get"]); let messageServiceSpy = jasmine.createSpyObj<MessageService>(["add"]); let service: HeroService; beforeEach(() => { TestBed.configureTestingModule({ providers: [ { provide: HttpClient, useValue: httpClientSpy }, { provide: MessageService, useValue: messageServiceSpy } ], }); service = TestBed.inject(HeroService); }); it('should be created', () => { expect(service).toBeTruthy(); }); });
変更箇所は、以下です。
import { HttpClient } from '@angular/common/http'; + import { TestBed } from '@angular/core/testing'; beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + { provide: HttpClient, useValue: httpClientSpy }, + { provide: MessageService, useValue: messageServiceSpy } + ], + }); + service = TestBed.inject(HeroService); - service = new HeroService(httpClientSpy, messageServiceSpy)
これも問題なく通るはずです。本題ではないので、説明は省きますが、何をやっているか分からない場合、以下を参照するのをオススメします。
HttpClientTestingModule と HttpClientTestingModule を使う
ここが今回の本題です。
以下のように変えます。
import { MessageService } from "./message.service"; import { HttpClient } from '@angular/common/http'; import { HeroService } from './hero.service'; import { TestBed } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; describe('HeroService', () => { let httpClient: HttpClient; let httpTestingController: HttpTestingController; let messageServiceSpy = jasmine.createSpyObj<MessageService>(["add"]); let service: HeroService; beforeEach(() => { TestBed.configureTestingModule({ imports: [ HttpClientTestingModule ], providers: [ { provide: MessageService, useValue: messageServiceSpy } ], }); service = TestBed.inject(HeroService); httpClient = TestBed.inject(HttpClient); httpTestingController = TestBed.inject(HttpTestingController); }); it('should be created', () => { expect(service).toBeTruthy(); }); });
変更箇所は、以下です。
import { TestBed } from '@angular/core/testing'; + import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; describe('HeroService', () => { - let httpClientSpy = jasmine.createSpyObj<HttpClient>(["get"]); + let httpClient: HttpClient; + let httpTestingController: HttpTestingController; ... providers: [ - { provide: HttpClient, useValue: httpClientSpy }, { provide: MessageService, useValue: messageServiceSpy } ... service = TestBed.inject(HeroService); + httpClient = TestBed.inject(HttpClient); + httpTestingController = TestBed.inject(HttpTestingController); });
とりあえずテストを通るか確認します。HttpClientTestingModule
, HttpTestingController
を使うとリクエストした内容の検証までできるようになります。次はそれを試すためにテストを追加します。
リクエストの内容を検証するテストを作る
以下のテストを追加して、通るか確認します。
it('サーバからヒーローを取得できる', async () => { const expected: Hero[] = [ { id: 11, name: 'Dr Nice' }, { id: 12, name: 'Narco' }, ]; service.getHeroes().subscribe(actual => { expect(actual).toEqual(expected); }); const req = httpTestingController.expectOne('api/heroes'); expect(req.request.method).toEqual('GET'); req.flush(expected); httpTestingController.verify(); });
解説
最初に期待値を作っています。この期待値は、同時にサーバから返ってくる値にもなります(req.flush(expected);
で行っています)。
そのあと、service.getHeroes()
を実行して、サブスクライブしています。データを受け取るとそのデータが期待通りになっているか検証しています(expect(actual).toEqual(expected);
の箇所です)。
この検証の処理は、まだ行われません。サーバからの応答は、HttpTestingController
で制御できます(厳密には、TestResult
?)。
先の処理で、リクエストは投げられたので、その内容が httpTestingController
で取得できます。const req = httpTestingController.expectOne('api/heroes');
がそれです。
expectOne
には URL を指定していますが、URL だけでマッチングさせるのが不都合な場合、独自に定義することができます。詳しくは、公式のドキュメントに委ねます。
expect(req.request.method).toEqual('GET');
は見たら分かる通り、GET や POST などの期待するメソッドになっているか検証しています。
req.flush(expected);
でサーバからの応答を擬似的に行っています。ここで指定した引数が actual
に入ってきます。この関数を抜けたあとにデータが流れて、expect(actual).toEqual(expected);
が行われるはずです。
最後に httpTestingController.verify();
で未処理のリクエストがないことを確認しています。 afterEach
で行うのが一般的みたいです。残っている場合は、テストが失敗します。
すごく雑な解説になりましたが、以上です。他にもエラーのテストや同じAPIの複数のリクエストなどの検証ができるみたいなので、知りたい方は、公式ドキュメントを参照してみるといいと思います。
最終的なコード
import { MessageService } from "./message.service"; import { HttpClient } from '@angular/common/http'; import { HeroService } from './hero.service'; import { Hero } from './hero'; import { TestBed } from '@angular/core/testing'; import { HttpClientTestingModule, HttpClientTestingModule } from '@angular/common/http/testing'; describe('HeroService', () => { let httpClient: HttpClient; let httpTestingController: HttpTestingController; let messageServiceSpy = jasmine.createSpyObj<MessageService>(["add"]); let service: HeroService; beforeEach(() => { TestBed.configureTestingModule({ imports: [ HttpClientTestingModule ], providers: [ { provide: MessageService, useValue: messageServiceSpy } ], }); service = TestBed.inject(HeroService); httpClient = TestBed.inject(HttpClient); httpTestingController = TestBed.inject(HttpTestingController); }); it('サーバからヒーローを取得できる', async () => { const expected: Hero[] = [ { id: 11, name: 'Dr Nice' }, { id: 12, name: 'Narco' }, ]; service.getHeroes().subscribe(actual => { expect(actual).toEqual(expected); }); const req = httpTestingController.expectOne('api/heroes'); expect(req.request.method).toEqual('GET'); req.flush(expected); httpTestingController.verify(); }); });
「TDD Boot Camp 2020 Online #1 基調講演/ライブコーディング 」を見た
見ながら Scrapbox に書いたことをここに貼り付けるだけのお仕事。
さすがに何度か見直さないと理解が追いつかなかった。
動画
TDD Boot Camp 2020 Online #1 基調講演/ライブコーディング
まとめた内容
最初に簡単な設計を行う
- どこかに何かの形式で今考えていることを書き出す
- 実装脳になると視野が狭くなる
- レッド、グリーン、リファクタリングを繰り返していると何をしているか忘れるため
- 「アジャイル開発では設計をしない」は間違い
- 常に設計し続ける
- 実装脳になってしまうので、現在の理解を書き出す
- TDD においては、TODO リストを作る
- 箇条書きで書き出す
- 例えば、10 項目書き出したら全てをテストを書くわけではない
- 動作するコードを書いてからきれいなコードを書くので、徹底的に問題を小さく分解して一つずつ解決していくやり方
- 例えば、10 項目書き出したら全てをテストを書くわけではない
- 1 個最初に倒すやつを選ぶ
- 選び方
- 重要度から選ぶやりかた
- テスト書きやすいものから選ぶやり方 TDDでは、最初はテスト容易性の高いものを選ぶ
- することが多いので、最初に重要度のものを選ぶと作業が重くなる
- テスト容易性の高いものと重要度が高いものは両立できる
- 重要なものと簡単なもの
- 重要なものと簡単なものは一致させることができる
- テスト容易性と重要度の4象限
- テスト容易性、重要度どちらも高いもの
- テスト容易性、重要度のどちらも低いものに寄せていくことができる
- 選び方
- 箇条書きで書き出す
- どこかに何かの形式で今考えていることを書き出す
TDDはテストファースト
- 利用者側の視点から実装していく(こう使いたい)
- テストを実行する(レッドになる)
- 実装者モード(グリーンにする)
- コードの綺麗さは脇においておく
- リファクタリング
- リファクタリングとは
- マーティン・ファウラー(Martin Fowler)が言ったのは
- 外部からの振る舞いを変えない
- 曖昧さを持った言葉
- 外部からの振る舞いを変えない
- 作者:MartinFowler
- 発売日: 2019/12/06
- メディア: Kindle版
- ケント・ベックは
- 成功しているテストが成功したままでコードを綺麗にすること
- プロダクトコード、テストコードどちらも含む
- 0,1 で判断できる
- 成功しているテストが成功したままでコードを綺麗にすること
- マーティン・ファウラー(Martin Fowler)が言ったのは
- リファクタリングとは
リファクタリングのやめどき
- 時間で区切る(何度でもチャンスはやってくる)
- 5~10分
- 数を数える
- 重複している箇所を共通の関数に抜き出す
- 一周するとたくさんのフィードバックがやってくる
- 目標を考え直す(TODOリストを改定する)
- 時間で区切る(何度でもチャンスはやってくる)
ライブコーディング(https://youtu.be/Q-FJ3XmFlT8?t=2504)
- 面倒くさいと感じる
- 問題が小さくない
- 面倒臭いと感じる部分を分解する
- 1-100まで
- 1からnまで
- あまり大事ではないのであとに回す
- プリントする
- 標準出力をテストするのは難しい
- その割に効果が薄い
- テスト容易性が低い
- ビューのテスト
- 壊れやすい
- テスト容易性が低いので、あとに回す
- 標準出力をテストするのは難しい
- 表現にブレがあるなら揃える
- 大事なとこのみに集中できるようになる
- 3の倍数のときは数の代わりにFizzにプリントする
プリントする
部分はテスト容易性が低、重要度も低プリントする
を切り離し、変換するに変える- 重要で、テスト容易性が高くなった
- 練習すればできるようになる
- ただし
- 準正常系
- 例外系
- 普通ではない振る舞い
- 正常系
- 1から100までの数をプリントする
- 数を文字列に変換する
- 1から100までの数をプリントする
- 面倒くさいと感じる
ライブコーディング前半(https://youtu.be/Q-FJ3XmFlT8?t=3376)
- 1周目と2周目は設計箇所が重い
- なにもないところから始めるため
- 設計の色合いが濃い
- 名前空間やファイル名を考える
- テストファイル名
- テストは入れ子構造にできる
- 対象クラスのテスト名にする
- 対象クラス名+テスト
- 一番最初はテスト環境がちゃんとできているか確認する
- そのためにテスト失敗のコードを変更して、失敗することを確認する
- テストコードは、動く仕様書であってほしい
- 母語でテストコードを書く
- プロジェクトに合わせる
- テストコードの構造
- 4フェーズテスト(3Aパターン(後片付けなし))
- 準備(Arrange)
- 実行(Act)
- 検証(Assert)
- 後片付け(言語によって必要)
- 振る舞い駆動開発
- Given
- When
- Then
- 4フェーズテスト(3Aパターン(後片付けなし))
- テスト駆動開発では、下から書く
- 検証がテストのゴール
- ゴールは期待値
- 軸をぶらさずにテストを書いていくことができる
- 検証がテストのゴール
- プログラミングの手が止まる
- できると思っていたのが、できない
- すらすら行くと思っていたが、いけない
- TODOリストに戻る(設計を考え直す)
- 早く手が止まるほうがいい(あとになると取り返しがつかない)
- テストは常に具体的になるので、なんとなくでは進めることはできない
- TDDでは、抽象と具体をいったりきたりする
- assertEquals は実測値と期待値がバラバラ
- 最初に確認する
- コンパイルエラーはレッド
- レッドは設計の駆動力
- 予想通りの失敗は悪くない情報
- 作る前に使う
- 作りやすいコードと使いやすいコード
- 作りやすいコードより使いやすいコードの方が大事
- 使いやすいコードは利用者から考えないと難しい
- 作りやすいコードと使いやすいコード
- テストコードのテストはどうする?
- ひどい実装
- メリット
- 設計に重きを置きたいとき
- 実装に頭を使わないようにする
- テストのテストのような厄介な問題を解決したい
- 設計に重きを置きたいとき
- デメリット
- 実装がひどくなる(汚いコード)
- 三角測量
- 仮実装に対して、別の値を与えることでまともな方向に戻していく方法
- テストを増やす
- メリット
- テストの増やし方
- タスクの分割のスキル
- 最初は難しい
- 最初はできない
- テスト容易性
- テストの書きやすさ
- テスト容易性とはどういう状態かを知る
- こういう状態だとテストが書きやすいを見つける
- テスト容易性
- オブザーバビリティ
- 観測容易性
- テスト対象のみやすさ
- 観察のしやすさ
- 制御のしやすさ
- 十分小さいか
- オブザーバビリティ
- 経験
- やってみるとわかってくる
- 設計の本を読む
- テスト容易性の高いとこや低いとこが判断できるようになる
- クリーンアーキテクチャ
Clean Architecture 達人に学ぶソフトウェアの構造と設計 (アスキードワンゴ)
- 作者:Robert C.Martin,角 征典,高木 正弘
- 発売日: 2018/08/01
- メディア: Kindle版
- 入出力から遠い
- プリントから近いとテストしづらい
- 理論から知る
- どっちも大事
- 最初は難しい
- [質問] 複数組み合わさったテストをしたいときは、そういうテスト用のメソッドを作るべき?それとも分解(のやり方)が間違っている?
- テストを増やしてリファクタリングしたい状況が出たとき
- 例えば、3箇所重複している箇所を発見した
- レッドならリファクタリングしてはいけない
- グリーンにする
- アンドゥでテストを除去する
- 今失敗しているテストを最短時間でグリーンにする
- グリーンにする
- テストからログを出力するのは良くない
- デモでは、テストの動きを確認するためにやっている
テストは、実行する順番が決まっていない
- プログラマーがテストの実行順序に依存するようなテストを書いてしまうのを防ぐ
- それぞれのテストが依存していないなら並列実行できるようになる
グリーンな状態を維持しながらリファクタリング
仮実装は三角測量を通らないといけないルールはない
- 実装者の不安レベルで間を挟むといい
テストの結果が予想通りなのはプログラマーにとっていいニュース
- プログラマーのコンディションがいいので、思い切った実装にすすめる
- テストや実装に不安がないのであれば大股で進めていい
- 失敗した(レッドになった)なら謙虚に進める(仮実装や三角測量などを利用する)
- 明白な実装
- テストや実装に不安がないのであれば大股で進めていい
- プログラマーのコンディションがいいので、思い切った実装にすすめる
テスト駆動開発には、3つのギアがある(https://youtu.be/Q-FJ3XmFlT8?t=6817)
- 1周目と2周目は設計箇所が重い
[質問] 外部仕様、ストーリー、ユースケース、インプットとしてTDDで開発するときは、E2Eで開発するのがいいか? xUnitを使うときにテストファーストで書けない
- E2Eやインテグレーションテストなどの外側のテストを書いてから内側のテスト駆動開発に進めていく
- 粒度の違う複数のテストを組み合わせていく
- 実戦テスト駆動開発
実践テスト駆動開発 (Object Oriented SELECTION)
- 作者:Steve Freeman,Nat Pryce
- 発売日: 2012/09/14
- メディア: 大型本
- ロンドンの流派
- 受け入れ、E2Eテストを先に書いてから中の実装をしていくやり方が書いてある
- E2Eやインテグレーションテストなどの外側のテストを書いてから内側のテスト駆動開発に進めていく
ライブコーディング後半(https://youtu.be/Q-FJ3XmFlT8?t=7005)
- 月日が経ってテストコードのタイトルを見てみると具体的な振る舞いはわかるが、別のパターンの場合にどうなるか分からない
- 次に何をしたらいいか分からない
- テストコードの実装を見てみる
- テスト対象がどうあるべきか伝わってこない
- プロダクトコードを見に行かないと分からない
- プロダクトコードにしか仕様が書かれていない
- テスト対象がどうあるべきか伝わってこない
- 動作するドキュメントになっていない
- TDDはまだ終わっていない
- テストコードをドキュメントにする
- 具体レベルのテストコードで残すと仕様書にはならない
- 実装するときはそれでもいいが、仕様書として残す場合、仕様レベルに直す必要がある
- 仕様レベルの名称にする
- 愚直なやりかた(言語によってはこれしかない)
- テストコード名を長くする
- 説明を仕様レベルと具体レベルの両方を書く
- テストコード名を長くする
- 愚直なやりかた(言語によってはこれしかない)
- 階層構造に作っていく
- 仕様レベルのツリー構造
- ツリー構造をTODOリストと合わせる
- 仕様の集合を分解していく
- ドメイン理解をツリー構造やテストコード名に適用する
- 仕様レベルのツリー構造
- 仕様レベルの名称にする
- 実装するときはそれでもいいが、仕様書として残す場合、仕様レベルに直す必要がある
- 具体レベルのテストコードで残すと仕様書にはならない
- テストの数をなるべく統一する
- テストの数の意味は、知らない人からは分からない
- 三角測量で追加したコードは、いらなくなる(一時的なテスト)
- メンテナンスコストになる
- 月日が経ってテストコードのタイトルを見てみると具体的な振る舞いはわかるが、別のパターンの場合にどうなるか分からない
テストの構造化とリファクタリング(https://youtu.be/Q-FJ3XmFlT8?t=8065)
- テストコードの約束の地
- そんなものはなかった
- テストのメンテナンスコストがかかる
- テストはメンテナンスコスト
- メンテビリティの低いテストコードが量産されている
- テストのメンテナンスコストがかかる
- そんなものはなかった
- 理解容易性が低いテストコードはメンテナビリティが低い
- 読みにくいテストコードは癌になる
- テスト駆動開発
- テストコードの約束の地
感想
大事な話が何個もあるので、たぶん時間が経つと忘れそう。
TDDの本を読んだことはあったけど、読んでいると自分の都合のいいように解釈してしまって、TDDはプログラマーが気持ちよく書けるようにするためのプラクティスなんだと少し思ってた節がある。ここで紹介しているように、テストコードが仕様として構造化されるとこまでリファクタリングするのがTDDだと説明していてTDDするときの姿勢を改めようと思った。といっても全然TDDできていないのだけど……。
仮実装や三角測量の話は、TDDの本読むだけだとあまり理解できなかったので、今回のライブコーディングで少し理解した気がする。
テストコードがあるコードでリファクタリングするとき、リファクタリング中はレッドになっていたので、今回の常にグリーンで進めていくのは勉強になった。今後は、グリーンを維持して書いていきたい。
仕事ではテストコードが必須ではないのでついプロダクトコードの実装から入ってしまって、どこかのタイミングでテストを書いたり書かなかったりするのだけど、やっぱりTDDを身に着けてTDDで実装を進めていきたいなぁと思い直した。
今回は話が濃いくてとても良いのだけど、TDDって何?ってのを知りたい人には長いかもしれない。そんな人は、以下の動画がおすすめ。こちらも同じ t-wada さんの講演です。
あとは、ここらへんもためになる(内容は組織に根付かせるとあるけど、個人でする場合も頭に入れておいたほうがいい話がある)。組織というか自分にテストの文化を馴染ませるために見てる。
リファクタリングする際、この本もためになるとコメントにあった(確か)。
- 作者:マイケル・C・フェザーズ
- 発売日: 2016/01/15
- メディア: Kindle版
コメント内容は忘れてしまったけど、ここらへんも紹介されていた。
テストとリファクタリングに関する深い方法論 #wewlc_jp
http://xunitpatterns.com/~gerard/AgileSingapore2016-slides.pdf
講演ありがとうございました!!!
「ランウェイで笑って」を見た
今年は何故かアニメをたくさん(主観)見ているのでせっかくだし見て面白かったアニメはここに記録していこうと思った。
とりあえず今回は「ランウェイで笑って」。
概要
原作
漫画(週刊少年マガジン)
あらすじ
身長158cmの藤戸千雪の夢は、 パリコレモデル。 モデルとして致命的な低身長を理由に、 周囲は「諦めろ」と言うが、 それでも折れない。 そんなとき、家族を養うために ファッションデザイナーの夢を 諦めようとする都村育人に出会う。
――これは一途に夢を追って 走り続ける、2人の物語。
こちらから引用
感想
二回見た。一回目を見終わったとき、まだランウェイで笑って
の世界に浸りたくなったので見終わった次の日にもう一度見ていた。このアニメの好きなところは以下
- 毎話先が気になる終わり方
- 暗い状況になってもあまり引き伸ばさずに前向きに解決する爽快さ(潔さ?)
- 千雪ちゃんかわいい
アニメの専門的な話はよくわからないけど、このアニメはシリーズ構成が良いように感じた。原作は知らないので原作通りに進んでいるかも分からないけど、ぽんぽん話が進んで飽きなかった。ただちょっと話を飛ばしているように感じる部分があった(日数の経過が早くて混乱することがあった)。
ショーモデル(ファッションショーで服を魅せるために歩く人)の話なのに、ショーモデルの作画に違和感があったので作画を気にする人は辛いかもしれない。でも全体的に良かったのであまり気にならなかった。
あとは出てくるキャラクター、特に自分の夢を目指そうとしてる人たちみんなが前向きな感情で動いてて良かった。後ろ向きな気持ちを持ってるキャラが出てくるけど、他のキャラが引っ張って前向きに動くような感じ。
見終わったあとに似たようなアニメを見たいと思ったけど見つかる気がしなかったのでじゃあもっかい見るかと。あと千雪ちゃんのかわいさを補充したかった。
最後の終わり方も続きが気になる感じで原作を読みたくなった。
おわりに
千雪ちゃんが自宅でテレビ見ながら仰向けに寝転んでるときのシーンがとても良かったです。
参考
Android のコンポーネント(Activity)の単体テスト3 - 別アクティビティからの結果を取得する
タイトル長いな……。簡潔に言うと onActivityResult
のテストをする方法です。
前回の続きになります。
pickles-ochazuke.hatenablog.com
概要
前回と同じ Espresso.Intents
を使います。Espresso-Intents については、前回話しているので飛ばします。
onActivityResult
は、requestCode, resultCode, インテントデータを受け取ります。この内、resultCode と インテントデータの2つのデータは、別のアクティビティが setResult()
で設定しています。この別のアクティビティが設定する部分をスタブ化します(スタブ化は、簡単に言えば、機能を真似するようにする。ということです)。
スタブ化するためには、Intents.intending()
とその戻り値である OngoingStubbing
のメソッド respondWith()
を使います。
次のように使います。
intending(hasComponent(OtherActivity::class.java.name)).respondWith(result)
使い方としては、intending()
の引数にスタブ化したい対象が一致する条件を渡し、そのスタブ化の対象が呼ばれたときに渡したい結果を respondWith()
に渡します。
上記だと OtherActivity
が呼ばれた(OtherActivity が作成された)ときに、そのアクティビティが終了されたものとし、result を onActivityResult() の各引数(resultCode, intent)に渡されます。
上手く説明出来ませんが、実際に使用すると感覚が掴めると思います。
実践では、前回のプロジェクトを使うので、なければこちらの ActivityUnitTestExample_2
ブランチを使うと同じ状態に出来ます。
実践
今回追加する処理は、前回のプロジェクトを使い、OtherActivity
を起動したら挨拶の情報を持ったインテントが返ってくるので、それをテキストに反映させるという処理にします。テストで確認するだけなので OtherActivity
側は何も変更しません。ですので、実際のアプリで OtherActivity
は何も返してきません。
まずは、別アクティビティから結果を受け取れるようにします。MainActivity.kt
を以下のように変更します。
import android.widget.TextView ... override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (resultCode != RESULT_OK) { return } if (requestCode != 1) { return } findViewById<TextView>(R.id.helloWorld).text = data?.getStringExtra("greeting") }
ついでに、startActivity()
から startActivityForResult()
に変更します。
startActivityForResult(intent, 1)
次にテストを追加します。
import android.app.Activity import android.app.Instrumentation import android.content.Intent import androidx.test.espresso.intent.Intents.intending @Test fun OtherActivityから受け取った結果がテキストに反映されるべき() { val intent = Intent().apply { this.putExtra("greeting", "Hi World!") } val result = Instrumentation.ActivityResult(Activity.RESULT_OK, intent) intending(hasComponent(OtherActivity::class.java.name)).respondWith(result) onView(withId(R.id.button)).perform(click()) onView(withId(R.id.helloWorld)).check(matches(withText("Hi World!"))) }
intending()
手前までは、ActivityResult
を作成しています。intending()
の引数は、スタブ化したい対象の条件を指定しています。そして、respondWith()
に ActivityResult
を渡しています。
https://developer.android.com/reference/android/app/Instrumentation.ActivityResult
スタブ化が完了したら、Espresso で UI を操作し、ビューが期待通りになっているか検証します。デバッグ実行を行い、onStartActivity()
と onView(withId(R.id.button)).perform(click())
にブレークポイントを設定するとが perform()
実行されたあとに onStartActivity()
で止まることが確認できます。
以上です。
ここまで行った状態が途中に上げた Github にあるリポジトリの ActivityUnitTestExample_3
ブランチです。
雑記
GW で調べていたことを放出しました。本当は Todo アプリを作って Android の理解を深めようと思ったのですが、その Todo アプリをテストしようとしたらいろいろエラーを踏んで3,4日潰れました……。まだテストのことは理解が足りていないので間違ったことを書いているかもしれませんが、誰かの助けになれば幸いです(間違っていれば指摘しただけると助かります)。
今年のGWは、技術書読み進めたり、Android のテスト調べたり、これ書いたりと割と充実していました(それでもやり残したことはありますが)。テストのエラー解決できないとき諦めようと思いましたが時間あけて考え直すと解決したので、やっぱり諦めずにいるのは大事だなと思いました(そして適度な休息!)。
あと恋する小惑星を見て地学に興味湧いたので NHK の地学基礎って動画見てるんですがかなり面白くてノートにまとめたりしています。