読者です 読者をやめる 読者になる 読者になる

レガシーコード生産ガイド

私に教えられることなら

Squeak Smalltalkで、未テストメソッドのテストを一気に追加

Smalltalk

相変わらずPetitParserでNirnLeaf用のコンパイラをちまちま書いている。

流石にテストが無いのが不安になってきたので、Test Runnerを使ってみることにした。

Test Runnerは、TestCaseのサブクラスをテスト対象クラスとして自動で追加して、testFooBarみたいに先頭にtestがついたメソッドを自動的に実行するみたいだ。

一方、PPCompositeParserのサブクラスでパーサを作っていて、それぞれの構文要素ごとにメソッドがある。既に30個ほどテスト無しの構文要素を作ってしまった。

最初はどの構文要素のテストを書いたかメモして一つずつ処理していこうかと思ったけど、やめた。そういうことこそコンピュータにやらせたい。

やりたいこと

あるクラスから特定のプロトコル内のメソッドリストを作る

クラス organization listAtCategoryNamed: #プロトコル名で抜き出せる。シンボルのArrayとして返ってくる。

testee := PhStGrammer organization listAtCategoryNamed: #parsing. 
"=> #(#assign #binaryMessageChain #binaryMethodName #binsym #block #blockArguments
      #cascade #colon #dot #expression #finalStatement #identifier #keyword
      #keywordMessageChain #keywordMethodName #literal #locals #localsDecl
      #messageChain #method #methodBody #methodName #nestedExpression #number
      #operand #reference #return #statement #statements #unaryMessageChain
      #unaryMethodName #verticalBar)"

fooBartestFooBarに変換する

いちから実装しても簡単だけど、せっかくなので既にあるものを探してみた。

まず'test fooBar' asCamelCase'TestFooBar'と先頭大文字のCamelCaseになって返ってくる。空白で分割した場所が単語の区切りとして処理されるみたいだ。 それに'TestFooBar' asLegalSelector'testFooBar'セレクタに使えるよう先頭が小文字になったものが返ってくる。

前の結果に適用した例

testee collect: [:name| ('test ', name) asCamelCase asLegalSelector].
"=> #('testAssign' 'testBinaryMessageChain' 'testBinaryMethodName' 'testBinsym'
      'testBlock' 'testBlockArguments' 'testCascade' 'testColon' 'testDot'
      'testExpression' 'testFinalStatement' 'testIdentifier' 'testKeyword'
      'testKeywordMessageChain' 'testKeywordMethodName' 'testLiteral'
      'testLocals' 'testLocalsDecl' 'testMessageChain' 'testMethod'
      'testMethodBody' 'testMethodName' 'testNestedExpression' 'testNumber'
      'testOperand' 'testReference' 'testReturn' 'testStatement' 'testStatements'
      'testUnaryMessageChain' 'testUnaryMethodName' 'testVerticalBar')"

あるクラスにプロトコルを指定してメソッドを追加する

ClassBehavior>>compile:classified:が手っ取り早い。

Foo compile: 'bar' classified: #bazで、Fooクラスのbazプロトコルに内容が無いbarメソッドを作れる。

今回は、ついでにstubの呼び出しをくっつけることにする。

テストメソッドを追加する

  1. テスト対象クラスの#parsingプロトコル内のメソッドリストtesteeをつくる
  2. テストケースクラスの#testingプロトコル内のメソッドリストtesterをつくる
  3. testeeを、それぞれのメソッド名シンボルの先頭にtestをつけたシンボルのリストにする
  4. testeeとtesterの差集合untestedを取る
  5. untestedのそれぞれのメソッド名でテストケースクラスの#untestedプロトコルメソッドを作る

例。テスト対象クラスはPhStGrammerで、テストケースクラスはPhStGrammerTest

"1" testee := PhStGrammer organization listAtCategoryNamed: #parsing. 
"2" tester := PhStGrammerTest organization listAtCategoryNamed: #testing.

"3" testee := testee collect: [:name| ('test ', name) asCamelCase asLegalSelector asSymbol].
"4" untested := testee difference: tester.

"5"
untested do: [:sel| |code|
  code := '{1} self stub' format: {sel}.
  PhStGrammerTest compile: code classified: #untested].

あるクラスのあるプロトコル内のメソッドを全て削除する

試してみて間違えたときは、クラス removeCategory: #プロトコル名で削除できる。

テスト実行例

テスト実行する度に、まだ内容を書いてないテストメソッドがこんな風にTranscriptに出る。

f:id:phaendal:20160429214005p:plain

(まあメソッド名変えたり削除したりしても反映できないので、最初の一回だけだけど……ブラウザなどツールで対応した方が良さそう)

広告を非表示にする