5.1さらうどん

@giginetの技術ブログ。ゲーム開発、iOS開発、その他いろいろ

XCTestCaseをswift-testingに自動変換するswift-testing-revolutionaryを作った

swift-testing-revolutionary

ほぼタイトル通りですが、先日XCTestで書かれたテストケースを、swift-testingのテストケースに自動変換する、swift-testing-revolutionaryというユーティリティを公開しました。Xだと流れてしまうため、せっかくなのでブログ記事でも告知しておきます。

自動変換されて便利

使い方

基本的にはREADMEをご覧ください。このツールは、Xcodeコマンドプラグイン、Packageプラグイン、Command Line Interfaceの3つの使い方をサポートしています。

超大雑把に以下のようなイメージを持っていただくと良いと思います。

導入方法 適応対象 導入の手間 カスタマイズ性
Xcodeコマンドプラグイン Xcodeプロジェクト
Packageプラグイン Swift Package
Command Line Interface 両方

アプリのテストケースなど、Xcodeプロジェクト内のテストケースを移行するにはXcodeコマンドプラグインとして利用するのがオススメ。GUIのみで簡単に導入、実行できます。

Xcodeコマンドプラグインの様子

ただし、Xcodeのコマンドプラグインの仕様が微妙すぎて、UnitTestターゲット全体しか変換対象に指定できませんGUIからファイル1つずつを変換対象に指定できれば便利だったのですが、Xcode側のサポートがされていないのでこれが限界です。

テストケース1つずつを変換したい場合は、CLIツールとして導入し、自分でファイル名を指定してもらうのが良いです。

$ swift-testing-revolutionary path/to/Tests/ViewModelTests.swift path/to/Tests/RepositoryTests.swift

また、プロダクトの性質上、一度変換してしまうともう使わないツールとなってしまうので、アプリケーション・パッケージの依存関係に含めるよりも、自分でバイナリを管理した方が利用しやすいかもしれません。Swift Packageをバイナリとして管理する方法は標準の良い方法がなく、現状Mintなどを使って入れるのが楽に思います。

swift-testingを導入していく

swift-testingの一番の利点は、既存のテストケースに変更を加えずに、今後追加するテストケースのみを簡単に新しいものに移行できる点です。また、簡単なブリッジングを書けば、Xcode 15.3 + Swift 5.10でも利用することができます。というわけでXcode 16を待たずとも今日から使えます!*1

今後は、新規テストはswift-testingで記入していくようにして、既存のテストコードの移行にこのツールを活用してもらえれば良いと思います。

ただ、このツールは既存のテストケースを機械的にswift-testingのコードに変換するだけなので、読みやすく、メンテしやすいテストに変更するにはさらなる書き換えは不可欠です。 (例えばParameterized Testsを利用するなど)

書き換えを行う場合も、このツールで一度機械的に変換してから、新機能の対応を行っていくと楽なため、ぜひ第一歩として役立てていただければと思います。

作ってみて

実装の大部分はswift-syntaxのSyntaxRewriterを使って、良い感じにシンタックスを書き換えただけ。

SyntaxRewriterの利用方法は、id:KishikawaKatsumi 先生のMastering SwiftSyntaxのセッションが大変役に立った。

expectationの置き換えなど、似たような変換が繰り返される処理は良い感じに抽象化して書けたと思う。

細かいところでは、Swift Syntaxにおけるtrivia*2の扱いに苦戦した。

例えば、テストメソッドにMacroやグローバルアクターのような予めattributeがついていて、さらに改行やスペースが含まれているケースを綺麗に置換するのがかなり大変だった。

@MainActor
func testCanFetchProducts() async await
@MainActor
@Test
func canFetchProducts() async await

その他、さまざまなシンタックスを考慮する必要があった。Swiftは言語機能が多すぎる。いろいろ抜け漏れがあるかも知れないので、気付いたらissueを立ててもらいたい。全体的に大分テストケースが足りていない。

このプロダクトは今年のGWに勢いで作り始め、概ね完成していたのだけど、当時はWWDC24前で、Xcode 16に同等機能が含まれていたらすぐにオワコンになるじゃん、とドキュメントまで書いて1ヶ月ほどお蔵入りにしていた。WWDC24のあと、Xcode 16に同等機能がないことを確認して、次の日にはすぐにpublicにすることができた。

ちょうどWWDC24期間中は現地にいたこともあり、パーティーで出会った開発者に挨拶代わりに紹介するなど、会話のきっかけになって良かった。

しつこいぐらいに宣伝しまくった結果、誰かがSwift Forumで言及してくれたりしていて、とてもありがたい 😊

こういうものを作ったら、あらゆるところで発信しまくるのが重要だなと再実感した。このエントリもその一環。

どうぞご利用ください

どうぞご利用ください。利用報告などお寄せいただけたら嬉しいです。

*1:ただしXcode 15で実行するとIDEサポートがないため、とても見づらい

*2:改行やスペースなど、意味解析には影響を及ぼさないtoken