Jestでテストを強制的に失敗させたい場合

fail() のようなメソッドが用意されているかと思ったけれど、ないっぽい。

https://stackoverflow.com/questions/42176642/in-jest-how-can-i-make-a-test-fail

StackOverflowおじさんたちは、テストを失敗させたいところで

throw new Error('failed');

としたり

expect(true).toBe(false);

としたりの力業に頼れと仰せだった。
Python慣れしていると微妙に不便。
throw が一番現実的かな。

__init__.pyがないディレクトリに出るエラー

基本的なことなんだけど、この手のエラーに関する日本語の情報が少なかったので書き残し。

pythonディレクトリを作るときは __init__.py が必須。当たり前の話だけど初心者なのでちょっとハマった。
__init__.py がなくても動く場合があるからややこしいんだけど)
ない状態だと、同じ構成の名前空間が同一パッケージ内にあると衝突する。

例えば、

root_dir
├ package_name_A
│ ├ views.py
│ └ tests.py
└ package_name_B
  ├ views.py
  └ tests.py

root_dir
├ package_name_A
│ ├ views.py
│ └ tests
│   └ tests.py
└ package_name_B
  ├ views.py
  └ tests
    └ tests.py

にしたいとき、それぞれの tests/__init__.py を置かないと、

____________ ERROR collecting root_dir/package_name_A/tests/tests.py ____________
import file mismatch:
imported module 'tests' has this __file__ attribute:
  C:\Users\user\Desktop\dir\root_dir\package_name_B\tests
which is not the same as the test file we want to collect:
  C:\Users\user\Desktop\dir\root_dir\package_name_A\tests\tests.py
HINT: remove __pycache__ / .pyc files and/or use a unique basename for your test file modules
 generated xml file: C:\Users\user\Desktop\dir\junit.xml

になる。
tests/tests.py が2つあるので衝突する。
もしこれが

root_dir
├ package_name_A
│ ├ views.py
│ └ tests.py
└ package_name_B
  ├ views.py
  └ tests
    └ tests.py

だったら問題なく動く。
ちょっと不思議。

GitHubにDraft Pull Requestが追加されたけど利用制限があるみたい

8/3追記
一部プランで使用できるようになってました。
GitHub Proの人が作ったリポジトリ+Privateで確認。
一方GitHub Free+Privateはまだ未対応でした。ほかはよくわかりません。情報求む。


2/15ごろからGitHubにDraft pull requestという機能が追加されました。
WIPを付けたPRを立てなくてもいい!と期待の機能でしたが、
いざ使おうと思ったらそんなボタンがどこにも存在しない。
よく見ると、全リポジトリに対して開かれた機能じゃなかったようです。

Pull Requestのヘルプページ

https://help.github.com/articles/about-pull-requests/#draft-pull-requests

によると、

Draft pull requests are available in public repositories with GitHub Free and GitHub Pro, and in public and private repositories with GitHub Team, GitHub Enterprise Cloud, and GitHub Enterprise Server.

とのこと。
日本語でまとめると、利用可否は下記のようになっていると書いてあります。

パブリックで対応, プライベートで非対応

パブリックもプライベートも対応

GitHub Proかつプライベートリポジトリでもダメとは。悲しみ…
自分の業務では恩恵に預かれませんでした。
対象リポジトリの拡張を待ちます。頑張ってGitHub先生…

MacOS 10.7 Lion でHTTPのページが開けない&設定からAppleIDを登録できない現象

スタートの状態

会社の先輩ら古めのMacBookを譲り受けた。
初期化済み&初期化後の確認のため、適当なrootユーザー作成済み。
osはMacOS 10.7 Lion。

発生したこと

  • HTTPSのページが開けない
  • 「設定」からAppleIDサインインできない(アプリDLできない、iCloud繋げない)
  • AppleIDサインインできないのでAppStoreからAppダウンロードができない
  • MacOSはAppStoreからダウンロードする必要がある
  • バージョンアップできない
  • Appleの公式サポートに、SSL通信の問題が起きたらバージョンアップしろと書いてあった
  • 詰み

解決方法

やったこと

1. 再初期化
2. ようこそ画面でID登録
3. AppStoreからEl Capitanインストール
4. Mojaveをインストール

※一度El Capitanを挟んだのは、10.7から直接Mojaveにバージョンアップできないため。
Mojaveに直接アップデートできるのは10.8以降。

変わること

  • 設定からどう頑張ってもAppleIDにサインインできなかったが、ようこそ画面からだと一発でサインインできた
  • →AppStoreからアプリを落とせるように→El Capitan、Mojaveがダウンロードできた
  • →バージョンアップしたらHTTPSにつながるようになった

原因

Apple公式でのQAを見るに、10.8.5以前のMacOSSSL周りが死んでるらしい。おそらくこれが原因
https://discussionsjapan.apple.com/thread/110210266?answerId=101111151122#101111151122

とりあえずMacは困ったら初期化

Node.js + Jestで「SecurityError:~」が発生した際の解決法

こんなエラーが出た

> jest

 FAIL  test\fileA.test.js
  ● Test suite failed to run

    SecurityError: localStorage is not available for opaque origins
      
      at Window.get localStorage [as localStorage] (node_modules/jsdom/lib/jsdom/browser/Window.js:257:15)
          at Array.forEach (<anonymous>)

突然出たエラー。これまでと特に設定は変えてない。なぜ?(。´・ω・)

見つけたページ

https://qiita.com/supaiku2452/items/972eb7e03414c695d033

でも` jest.config.js `というファイルはない。どこに書けば良いかちょっと迷った。

最終的な解決方法

https://github.com/facebook/jest/issues/6769
https://doc.ebichu.cc/jest/docs/ja/configuration.html

package.jsonに書けばいいらしい。
既に存在するルートディレクトリのpackage.jsonに "jest" を追加する。

{
  "name": "my_application_name",
  "version": "1.0.0",
  "description": "my application description",
  "jest": {
    "verbose": true,
    "testURL": "http://localhost/"
  },
  "main": "index.js",
  
  ...

適当なところに追記しました。
これでJestを再実行するとテストが通った。

解決!

Puppeteer入りNode.jsアプリをServerlessでAWS Lambdaにデプロイするときに困ったこと

結論

nodejs+puppeteer+lambdaのアプリケーションには .npmrc が必須

起きたこと

C:\[hogehoge]>npx sls deploy

をしたら

  Serverless Error ---------------------------------------

  An error occurred: [Function Name] - Unzipped size must be smaller than 262144000 bytes (Service: AWSLambda; Status Code: 400; Error Code: InvalidParameterValueException; Request ID: [fugafuga]).

と怒られました。
Serverlessのログを見てみると、

~略~
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (157.42 MB)...
Serverless: Validating template...
~略~

157MBのzipをアップロードしようとしている。
Lambdaの容量制限は50MBなのでそりゃ怒られる…

調査したこと

ディレクトリのサイズを調査すると、node-module で300MBくらい食ってることが判明。
何度か npm install を繰り返して原因究明。
アプリケーションに使用していたPuppeteerの中にChromium(130MBくらい)が含まれていた。こいつのせい!
こいつがデプロイの時に含まれないようにしたいと思い、除外設定を探すもヒットせず。

解決法

たどり着いた神記事がこちら
https://www.pressmantech.com/tech/serverless/4613

ちょっと自分の対応の角度はズレてた。
解決法は「デプロイに含めない」ではなく、「そもそもインストールしない」だった。

というわけで、↑の記事に従って以下の手順を踏んだ。


1. node-moduleディレクトリを全削除

2. .npmrcをルートディレクトリに作る(書くのは1行だけ)

PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=TRUE

3. npm install

これで skip install が出た。
もう一回デプロイ挑戦したらzipのサイズは130MBくらい減っていて、無事lambdaにアップロードされていった。
ありがたや。

Djangoで空文字・Noneを許容する文字列Validateを作る

やりたいこと

DjangoのSerializerで、
空文字とNoneと、ついでに未入力も許容する文字列バリデータをつくる

結論

CharFieldを定義するときに、
requiredだけでなく allow_blank, allow_null も設定しよう(でも罠がある)

class TestSerializer(serializers.Serializer):
    hoge = serializers.CharField(required=False, allow_blank=True, allow_null=True)
>>> serializer = TestSerializer(data={})
>>> serializer.is_valid()
True
>>> serializer2 = TestSerializer(data={'hoge': ''})
>>> serializer.is_valid()
True
>>> serializer3 = TestSerializer(data={'hoge': None})
>>> serializer.is_valid()
True

やってみること

テストのためにシリアライザを用意しました。

class TestSerializer(serializers.Serializer):
    hoge = serializers.CharField()

ここから ""(空文字)、None、そもそも値がない の3パターンが通るようにしていきます。

今の状態でバリデーションをかけてみる
>>> from fuga.serializers import TestSerializer
>>> serializer = TestSerializer(data={})
>>> serializer.is_valid()
False
>>> serializer = TestSerializer(data={'hoge': ''})
>>> serializer.is_valid()
False
>>> serializer = TestSerializer(data={'hoge': None})
>>> serializer.is_valid()
False

すべてFalseになります。

試したこと

CharFieldには状態の異なる「空」を許容するオプションが3つあります。
それらを全て許容状態にしていきます。

1. required

required はすべてのフィールドクラスに共通するバリデートで、
”引数がシリアライザに渡されたかどうか”だけを見ます。
どんな値が入っているかはチェックしません。
デフォルトはTrue。

class TestSerializer(serializers.Serializer):
    hoge = serializers.CharField(required=False)
>>> serializer = TestSerializer(data={})
>>> serializer.is_valid()
True

これでまず、そもそも値が存在しない場合がクリア。

2 allow_blank

allow_blank は空文字とNoneを許容するバリデートで、デフォルトはFalseです。
CharFieldでは allow_null が非推奨で、こちらが推奨されています。

class TestSerializer(serializers.Serializer):
    hoge = serializers.CharField(allow_blank=True)
>>> serializer = TestSerializer(data={'hoge': ''})
>>> serializer.is_valid()
True

>>> serializer2 = TestSerializer(data={'hoge': None})
>>> serializer.is_valid()
True
3. allow_null

allow_null はすべてのフィールドクラスに実装されているバリデートです。
nullを許容するか否かだが、CharFieldでは空文字かnullかの判定になります。
デフォルトはFalse。
上で述べたように、CharFieldでは非推奨とされているようです。

class TestSerializer(serializers.Serializer):
    hoge = serializers.CharField(allow_null=True)
serializer = TestSerializer(data={'hoge': None})
serializer.is_valid()
True

serializer2 = TestSerializer(data={'hoge': ''})
serializer.is_valid()
True

注意点

allow_blankとallow_nullは上で書いたように、
一つずつオプション指定をすると、
空文字とnullの両方を許容する挙動でした。
しかししかし、required=False と組み合わせると、
allow_blankはnullを、allow_nullは空文字を許容しなくなりました。
どういうことなの……

class TestSerializer(serializers.Serializer):
    hoge = serializers.CharField(required=False, allow_null=True)
s = TestSerializer(data={'hoge': ''})
s.is_valid()
False
s2 = TestSerializer(data={'hoge': None})
s2.is_valid()
True
class TestSerializer(serializers.Serializer):
    hoge = serializers.CharField(required=False, allow_blank=True)
s = TestSerializer(data={'hoge': ''})
s.is_valid()
True
s2 = TestSerializer(data={'hoge': None})
s2.is_valid()
False


Serializer fields - Django REST framework
では、

It is valid to set both allow_blank=True and allow_null=True, but doing so means that there will be two differing types of empty value permissible for string representations

とあるため、2つセットしたときの挙動のほうが正しそう…?な気がします。
ただ、2つ使う時はバグに気をつけろ的記述もあるため注意したいところ。

上記の挙動を踏まえて、とりあえず今回はallow_blankもallow_nullもセットしましたが、
厳密なチェックを行う場合は注意してください。

感想

required=Falseにすれば、値がないのも空文字もNullも等価な世界線から来たので、
required=Falseにしてるのにバリデートが通らなくて結構苦戦した…
Django触り始めて1か月くらい、知れば知るほどDjangoさんは奥が深い。

しかしなぜオプションの数で挙動が変わるの…?今度調査したい。します。がんばる