みーのぺーじ

みーが趣味でやっているPCやソフトウェアについて.Python, Javascript, Processing, Unityなど.

reCAPTCHAを使用するフォームを自動でテストする

Google reCAPTCHAを使用することで,ボットがフォームに機械的なデータを送信するのを防ぐことができますが,フォームが正しく動作するかを確認するためにテストをすると,機械的なデータを送信することになり,フォームを通過できません.このどうしようもない矛盾について,妥協点を探ることにします.

要件

  • reCAPTCHA v3を使用したフォーム
  • TestCafeでテストを自動化する

reCAPTCHA  |  Google Developers

reCAPTCHAのドキュメント

I'd like to run automated tests with reCAPTCHA. What should I do?
For reCAPTCHA v3, create a separate key for testing environments. Scores may not be accurate as reCAPTCHA v3 relies on seeing real traffic. Frequently Asked Questions  |  reCAPTCHA  |  Google Developers

簡単に訳します.

reCAPTCHAを使用したフォームに対して自動でテストを実行するにはどうすればよいですか?
reCAPTCHA v3をご利用の場合,テスト環境で用いる別のキーを作成してください.実際の通信内容によってスコアが計算されるため,正確ではないかもしれません.

つまり,テスト環境用のキーを作成し,スコアがどんな値であっても通過するようなサーバーを用意すればよいみたいです.

TestCafeのドキュメント

TestCafeが利用しているtestcafe-hammerheadによって,任意のWebサイトが適切にproxyされるようですが,sandboxによってreCAPTCHAと相性が悪いようです.

We know some of the potential issues associated with our sandbox. Since there is the official reCAPTCHA recommendation, we have no plans to continue our research of this issue.

Web sites using Google Recaptcha V3 fail in testcafe-hammerhead · Issue #2223 · DevExpress/testcafe-hammerhead · GitHub

TestCafeがreCAPTCHA v3をサポートする予定はないようです.

妥協案

productionサーバーのみでreCAPTCHAを有効にし,TestCafeを実行する対象のstagingサーバーではreCAPTCHAをdummyにします.

例えばdjango-recaptchaを使って以下のフォームをテストする場合を考えます.

from captcha.fields import ReCaptchaField
from captcha.widgets import ReCaptchaV3

class ReCapchaSignupForm(SignupForm):
    captcha = ReCaptchaField(widget=ReCaptchaV3)

RECAPTCHA_REQUIRED_SCOREが0であればreCAPTCHAを無効にするフォームに変更してみます.

from captcha.fields import ReCaptchaField
from captcha.widgets import ReCaptchaV3
from django import forms
from django.conf import settings

is_recaptcha_enabled = getattr(settings, "RECAPTCHA_REQUIRED_SCORE", 0) > 0

def getReCaptchaField():
    if is_recaptcha_enabled:
        return ReCaptchaField(widget=ReCaptchaV3)
    return forms.BooleanField(widget=forms.HiddenInput(), required=False, initial=True)

class ReCapchaSignupForm(SignupForm):
    captcha = getReCaptchaField()

これで,RECAPTCHA_REQUIRED_SCOREが0であれば必須ではないBooleanFieldに置き換わるため,テスト環境では常に通過します.

<input type="hidden" name="captcha" value="True" id="id_captcha">

上記のhtmlが出力されるため,TestCafeでは#id_captchaがフォームに存在することがreCaptchaが導入されていることの証であると判断すればよさそうです.

最後に,RECAPTCHA_REQUIRED_SCOREを0より大きな値に設定しても人間がブラウザーを通じてアクセスした時に,reCaptchaを通過できるかを確認します.このプロセスは,reCaptchaの性質上どうしても自動化できない部分だと思われます.

まとめ

自動でテストを実行するのは便利ですが,reCAPTCHAのように相性が悪いテクノロジーを別途処理する手間が増えるので悩ましいです.また,dummyなどを多用しすぎると,productionとstagingが乖離してしまうので注意が必要です.