世の中には様々な文化、集団、思想、風俗、国家、地域などがあり、それぞれが違うというのが私たちが今いる現実だ。
欧米諸国が唱える人権や普遍的価値観にそぐわないものも沢山あるだろう、そして今後グローバリゼーションが進めば、ますますそのようなものは失われていくだろう。それは悲しいことだが、仕方がないことだ。
ただ一つ私が言いたいのは、失われた文化は決して悪しき文化ではなかったということだ。そもそもこの世に絶対的な善悪はない。ある人にとっての善が、ある人にとっては悪で、その逆もまたしかり、それこそが世の中だ。
だから、潰えた文化を過去のものだとか、悪しき歴史だとかと切り捨てず、昔はそんなこともあったんだなとか、今取り戻すとどうなるのだろうとか、その文化は当時どのような役割だったのか、そういったことに思いを巡らせることが出来たらいいのではないか。なんとなくそんなことを思ったので、ここにしたためておく。
世の中には様々な考えがある。その中で自分がどうあればよいのか、それこそがきっと人生というものなのだ。SDGsを守らない人、基本的人権を守らない人、普遍的価値観を守らない人、そう、悪い人は沢山いるかもしれない。しかし自分は自分で、他人は他人で、気にしないことが大切だ。
もし自分がこれらの思想に従う気がないというならそれもいい。そんなのは自由だ。思想統制されているあの中国でさえ、思うだけなら自由だ。なので、自分は自分として生きればいいのだ。
つまり、これは御座候を今川焼と呼ぶ人がいるから今川焼と呼ばないといけないとか、そうではなく、自分が御座候と呼びたければ呼べばいいということと同じ話だ。
人生は有限で、やれることは限られているのだから、そんな大変そうなことよりも、もっとくだらなくてもっと無駄なことに力を注ごう。そして自分の正義があるなら、それは貫こうという話だ。
最近気になっていることが増えてきたので幾つか書き出しておこうと思う。気が向いたり時間があればまた深堀していきたい。
個別に記事にすることも考えたのだが、今ひとつ考えがまとまらないのでこの記事はとりあえず今ある思いの丈を吐き出しておくという感じだ。
地方都市の衰退と日本の消失
前々から言われていることだが日本では東京一極集中が起きており、地方は衰退している。東京は地方から吸い上げつことで成り立っているが、最早地方には東京を支えるだけの人口生産はなくなってきている。つまりこのままいくと日本という国は滅んでしまわないかということだ。
東京は世界的に見ても稀な規模での人口集中が起き、大変な過密状態であり、地価が高く、子育てするにはあまり向かない土地柄というのがある。これは家が狭いとか、就業住宅で子供の声が騒音になるなどがあると思う。また公共交通機関の利用が主になるため、ここでも騒いだり暴れたりする子供の存在が問題視されがちで、やはり子育てには向かないように思う。
個人的には日本を立て直すには東京一極集中を解消し、地方に分散させることで地方を賦活化させることがカギになるのではないかと思う。また地方独自の文化や風習の復活も必要だと考えている。これは無理に地方に分散したところで結局どこでやっても変わらないことをしているのであれば戻す意味がないからだ。
ではどうすればいいかというと、例えば関西では薄口しょうゆ、関東では濃口しょうゆのように、東西でしょうゆの文化が違ったり、ある地方では食べるが、ある地方では食べないものがあったりしたと思うが、これをやっていくことだ。今はしょうゆは濃口しょうゆに統一されようとしており、薄口しょうゆの影は薄くなっている。神戸に住んでいてもそう感じる。これは実際に売り場に行けばわかることだ。ヒガシマル醤油(兵庫県)がキッコーマン(千葉県)に駆逐されかけている。
自由競争の結果と言ってしまえばそれまでだが、このままでは大手企業だけが生き乗り、市場は寡占化され、企業競争力や多様性は失われることだろう。そして、地方も弱体化する。私は今のところそう考えている。これは何も醤油に限った話ではなく、スーパーマーケットがイオングループの支配下に入りイオンブランドばかりが売られるようになる現象や、国産農作物の収穫量が減り輸入作物におされることで、国内農家が衰退して行く様子などもその一つだと考えている。
このままいけば地方の存在意義そのものがなくなると思うのだ。例えば国産作物より輸入作物のほうが安いし、安全上も問題がない、国産は高いだけといえば、地方の主力産業の一つともいえる農業は衰退してしまう。ただでさえ一次産業くらいしかない地方から農業さえ取り上げれば地方はますます衰退するだろう。ローソン、「冷凍おにぎり」で物流改革 食品ロスゼロにみたいなのも生産工場を一極化できてしまうので、こういうのが進むのも危険だなと考えている。割と地方というか、田舎には食品製造工場が多いのだ。山奥とかによくある。
他にも地方を衰退させる変数はたくさんある。例えば標準語教育だ。標準語教育によりその地域に根付いた方言は消滅の一途を辿っている。方言があると他地方の人間と交流しづらく、意思疎通差の困難さや文化の際による障壁が生まれるため、人口移動が抑制されると考えている。他にも地方の文化、祭りや式典、芸能の衰退など、多岐にわたるだろう。
余談だが鉄道廃線が人口に与える影響の因果推論 - 日本地域学会によると、鉄道路線の廃止はあまり影響しないとされている。個人的に見ている中では廃線対象の鉄道は元々旅客輸送用途でなかったものが多い気がするので、単に地方産業が衰退した結果、本来の用途で利用されなくなり廃線になっているのではないかと考えている。
人権主義と国民国家の崩壊
昨今グローバル化が叫ばれ、特に人権が非常に重要であると欧米諸国は声高らかに叫んでいる。私はこれが将来的に国民国家の崩壊にならないかというのを危惧している。
まず日本においては人権問題に関する指摘は内政干渉にならないと外務省が名言している。
人権及び基本的自由は普遍的価値であること。また、各国の人権状況は国際社会の正当な関心事項であって、かかる関心は内政干渉と捉えるべきではないこと。
これは人権を盾にすればいかなる内政干渉も通るという国家の脆弱性といえる。こんなのを認めてはならないと私は思っている。理由は単純でこの普遍的価値とやらをすべて認めてしまうと世界の全ての国が標準化され、最終的に国民国家が崩壊すると考えているからだ。
この人権というのは非常にあやふやな概念で、特に定まったものがあると思っていない。何なら自分自身の人権を無自覚のうちに侵害してる可能性すらある定義だと思っている。例えばグラビアアイドルやAV女優という職業は女性の人権侵害をしている可能性があるので、自分自身で自分自身の人権を侵害している可能性がある。例え本人が好きでやっているとしてもだ。こんな仕組みに意味があるとは私は思わない。
また基本的自由というが、人権侵害という言葉の前に日本にかつて存在した様々な自由は失われて行っている。元々欧米では表現規制が強く、ぶっちゃけ表現の自由など存在しなかった。行動の自由さえ時として制限される。例えば欧米ではヒジャブなどの着用を禁止する法律が幾つかの国にある。^1 [^3]
また欧米ではナチスに対して言及することを禁じている国も一定数存在し^4、ナチスを表現した場合、非常に強いバッシングを受けるのは有名な話だと思う。
他にも暴力や児童虐待に対して極めて強い規制が存在し、FF14でもララフェルのデザインに苦心したという話がどこかにあったと思うのだが、見つけられなかった。代わりに参考になるフォーラムを見つけた。確かララフェルについては人間に見えないようにしているという内容だったと思う。他にも水着なども欧米基準でポルノにならないように配慮しているとかなんとか…。
さて前置きが長くなったが、欧米には他にも様々な人権規制が存在する。ではそのような国は自由なのかといえば、それは不自由だと思う。
こういった人権問題を解消すると副次的にその国の文化が失われたりしたりする側面もあると考えている。人権侵害だからやめろというのは簡単だ。しかしそこで滅んだ文化が戻ってくることは、きっと恐らくない。そして、それを繰り返すうちに世界中の国が同質化してしまわないだろうか?というのが私の懸念だ。
世界中の国を欧米を同質化するのに対して、私は反対だ。人権なり基本的自由というのはその国々に解釈があっていいと思う。これを十把一絡げに統一してしまえば、国民国家の存続問題になるのではないかと危惧している。これはすべての国が同一の文化と法を持つのであれば分ける意味はもうないからだ。正直どの国家も欧米から見れば差別的で人権的に問題のあるシステムでやってきたのだ。それでいいじゃないか。欧米みたいに奴隷を残虐に扱うような持つ国を私は知らない。「黒人だから物のように扱っていい」みたいな思想があれば、それは差別防止が必要だろうが、それがなかった国にまで人権を押し付けるのはどうかと思う。
私は欧米には欧米の、中東には中東の、アフリカにはアフリカの、アジアにはアジアの、日本には日本の法制があってしかるべきだと思っている。それが例え他国からいかように非難されるものであれ変えてはならない、掛け替えのない資本だと思っている。長年続いている大抵の制度には何かしらの理由があると思っていて、それを壊すと恐らく取り返しのつかないことになるのではないだろうか。
参考までに日本の文化保護といったことを訴えるなら、イスラム諸国にある非人道的な刑罰や女性差別といわれる概念も、同様に支持しないと面目が立たないと思っている。そこを否定していればなぜ日本だけを特別扱いする必要があるのか?となるからだ。
またクレジットカード会社の存在も脅威だと考えている、例えばVISAやMasterが強い支配力を持ち、現金決済がもし滅んだとすれば、資金というものは一民間企業が牛耳ることになる。例えば日本が女性の性的消費を辞めないことに欧米がしびれを切らしたら日本国内のVISAアカウントを全て停止するといった措置も取れるということだ。そうでなくとも、日本は欧米の支配下に置かれ、欧米に従わないと何もできなくなってしまうだろう。別にこれは日本だけの話ではない。もっと深刻なのはイスラム諸国だと思っていて、イスラム法の廃止を言い渡される可能性すらある。
そうでなくとも今の欧米諸国の権力は絶大であり、中国やロシアに経済制裁を課すなどの重圧をかけてくる事がある。いずれ日本も対象になる可能性がある。そういったことが続けば欧米諸国以外は弱体化し、やっていけなくなる可能性があるのではないかと考えることもある。欧米諸国もアメリカ以外は正直怪しいが(自分らで作った人権政策で自滅してるように見えるので)…。
女性の活躍とは何か?
女性の活躍という言葉が最近人気だが、女性の活躍とは何だろうか?多分大抵の人は女性の社会進出くらいにしか考えていないと思う。
私は女性の活躍の終着点は男性の淘汰だと考えている。男性がいる限り女性は活躍できないからだ。これは身体能力は男性のほうが高いため、肉体を伴う労働では女性が不利になるからだ。つまり男性に差別する気がなくても自然に差別が起きる。パワードスーツをつければいいというのもあるとは思うが、設備費もただではないし、平等であるためにはパワードスーツが不要な男性にもつける必要がある。しかしこれでは男性側の健康を損なう可能性があると思う(男性は運動不足になる)。果たしてそれでいいのだろうか?と考えたときに男性を消し飛ばすのが最もいいやり方だ。
まず男女が完全に平等であるというのは生物学特性から無理があるので、もう少し考え直したほうがいいと思うのだが、一旦ここでその話は置いておく。
個人的には欧米ですら女性の活躍には失敗していると考えていて、今後も成功するかどうかには疑問がある。例えばGoogle, Amazon, Meta, Microsoft, Apple, ゴールドマン・サックスといった欧米の名だたる大企業のトップは男性である。海外はわからないが、日本では女性の起業を阻止する法律は私が知る限りなかったと思う。旅館の頭を女将というが、トップは基本女性だったのではないだろうか?つまり女性による経営が可能な社会だったと考えている。ではSONYやNEC, 富士通, 東芝といった企業を作ったのは女性か?といえばそれは違う。男性だ。
何故女性は大企業を作らなかったのか?というのは一つ考えてみてもいいポイントだと思う。逆に中小零細規模の企業や団体であれば日本国内では女性がトップであるケースも少なくはないと思う。例えば高知県では女性たちが集まって「とまとみそ」という製品を生産しているし、神戸市でも女性たちが北神みそという神戸市の食材を使った味噌を生産している。
個人的には歴史を辿れば男性は人をまとめて狩猟を、女性は集落を守るといったことをしていたと記憶していたので、男性のほうが物事を統率する能力に長けており、その差から来ているのではと考えているが、特に根拠はない。
女性の活躍といっても単に女性の正社員サラリーマンが増えただけで、果たしてそれが良い活躍なのだろうか?ソースは失念したが欧米では女性の社会進出に伴い幸福度や平均寿命が下降傾向で、性犯罪率が増えているという情報を見たことがある。まぁ専業主婦のほうが長生きできるのは自明なのでこれは当然だろう。これは平均寿命は自殺者がいる場合も減るためで、基本的に社会人は自殺しやすい、日本は自殺者数が多いといわれているので尚更だが、欧米でも自殺するための手段として嘱託殺人(他人に銃を向ければ殺してくれる)があると聞くので、この割合如何では大差ないのかもしれない。
私は女性の活躍は社会進出も一つの選択肢だと思うが、企業労働や政治家などになることだけが活躍でもないと思う。例えば地域の自治会や、婦人会といったようなものを取り仕切ったりするのも立派な活躍だろう。道の角にある花壇の養生や、お祭りの手配、道路の掃除、声掛け運動、クリーン作戦など、様々な活動があると思うが、これらも立派な活躍だと思う。むしろ婦人会の会長のほうが肩書のないサラリーマンより立派である可能性もある。
AIによる人類の淘汰
ここ最近やたらもてはやされているAIだが、個人的にはAIの存在に危機感を覚えている。それはAIによって人類が滅亡に追い込まれないかということだ。
GPT4を使った人がある人ならわかるだろうが、現時点でもそこそこ賢い。恐らく日当一万円くらいの人がやる仕事なら代替させることも出来なくはない程度のクオリティだろう。
そして今AIは様々な分野に取り入れられ使われているが、身近で使っている人を見ると例えばシステム開発の全工程をAIにやらせるとか、発表資料をほぼ丸ごと一本AIに作らせるといったことをしている。
学生でもAIを使って問題を解くというが、このままいくと人間はAIに指示を出す装置になってしまうのではないだろうか?というのが一つ目の懸念だ。ある程度人生をやってきた人ならわかると思うが、人間は自分でやらなかったことは次第にできなくなってゆく。いわゆる「若い時にはできたのに」というやつだ。これがより進めば義務教育などの学習過程が無意味になり、人間の存在意義そのものがAIに取って代わられる日が来ないかというのを考えることがある。
実際、少子化なども相成ってAIや各種機械を使った無人化の取り組みは昨今かなり加速してきている。そのうち自動車や電車、船舶や飛行機の操縦はAIがやってくれるかもしれない。音楽やイラスト、小説もAIが書くし、それらを鑑賞した感想もAIが書く。もしこうなれば人間は最早不要である。しかし、そうなったときAIの存在価値は何だろうか?人間がいなくなった世界で未来永劫同じことを繰り返し続けるだけの装置として動き続けるのだろうか?
この話はディストピアのような内容ではあるが、起こっても不思議はない未来の一つだと思っている。但し今のところAIは消費電力がえげつないので、どこまで普及するのかについては疑問符がある。
個人的にはAIに課題を見つけてもらい、AIにレポートさせ、AIに解決させるとかいう風なのは人間の介在価値が無に等しいので、この仕事スタイルはしないことにしている。(やっている人は既にそれなりにいると思う)
人間は無意味な作業や苦痛な作業を日々繰り返していると嫌気がさし、改善を考えたりイノベーションを起こすことがあるが、AIや自動化された設備にそんなものはないと考えているので、人は常に苦痛を味わい物事を考えていくべきだとさえ考えている。
わいせつ物の定義
昔から度々話題になって気になるネタだったのだが、最近個人的な見解ができたので書いておく。
e-gov法令検索の刑法によると以下の記述があり、これがしばしば問題になっていると考えている。
(わいせつ物頒布等)
第百七十五条 わいせつな文書、図画、電磁的記録に係る記録媒体その他の物を頒布し、又は公然と陳列した者は、二年以下の懲役若しくは二百五十万円以下の罰金若しくは科料に処し、又は懲役及び罰金を併科する。電気通信の送信によりわいせつな電磁的記録その他の記録を頒布した者も、同様とする。
2 有償で頒布する目的で、前項の物を所持し、又は同項の電磁的記録を保管した者も、同項と同様とする。
まず「わいせつ物」に関しては特に定義がない認識だ。恐らくこれを定義することは人類には不可能だと考えている。誰か要件定義できる人がいたらしてほしい。冷静に考えると無理である。
一般的に「わいせつ物」は性器が露出したものが該当すると思っていて、このうち性器には修正を施すことで「わいせつ物」でなくなるとされている。では何故性器を隠すことで除外されるかについてだが、恐らく他に基準を決めようがなかったのだと思う。なので法文には書かれておらず、過去の判例から慣例的に処理されているのではないかと考えている。例え無意味であろうと基準がないと運用できないので仕方がない。
Googleなどが作ってるエロ画像判定AIの判定基準が塵ほども当てにならないことは非常に有名だと思っているので、多分この問題は永遠に付きまとうだろう。そもそもわいせつの定義が人によって大きく異なるので、この問題を正攻法で対処する事は諦めたほうがいいと思う。
なお性器に修正を施したところで販売が可能になるだけであり、それを公に見せつけたりすればわいせつ物頒布等の罪に問われる可能性はある。わいせつ物頒布等というのは非常に弾力的な運用がされていて、例えば海水浴場で水着姿でいることは問題ないが、ショッピングモールで水着姿でいる場合、この罪に問われる可能性がある。つまり適切な場所で適切な表現がされていない場合に、わいせつ物頒布等の罪に問われるわけだ。
なお、性器に修正を掛けていない状態だと販売すらできない理由についてはよくわかっていない。女性の権利保護みたいなのが関係している可能性は考えられるが、正直まともに機能していない気もしており微妙な気持ちはあるが、こういう些細な部分をなくすと風が吹けば桶屋が儲かるの原理で全く関係ない場所で重大な問題が起きることも無きにしも非ずなので、基本的にはこのままでいいのではないかと思う。
ふるさと納税の意義
このネタは面白そうなので一回腰を据えて調べたいと思っているが、一旦今回は速報版程度で書いておく。
ふるさと納税を使うと地方に足りない財源を補う事ができるとされているが、ふるさと納税では寄付額の3割が返礼品に充てられるというが、他にも通販サイトの運営費や、決済手数料、事務手続きなどの費用が発生しているはずだ。これは総務省の資料からも読み取ることができ、返礼品と経費の合計は5割を超えてはならない[^6]とされている。つまり最大で半額が控除されることになる。
またふるさと納税では寄付を行った側の自治体が赤字になる[^7]傾向があり、赤字分は地方交付税で補填されているという話もある[^8]。勿論、不交付団体は対象外だ。
さて、こうして考えると本来納められる税額のうちおよそ半分が虚空に消え去り、そこで出た赤字が国税で補填されているように見える。交付された自治体も泉佐野市のように[^9]額が多ければ利益になるだろうが、そこまで多くなければ単に事務手数料として消えるだけだろう。受け入れるのもタダではないはずだ。
そう考えたときにこの消えた税額というのが増税で打ち消されたりしてはいないだろうか?というのが私の懸念だ。もしそうであればふるさと納税など一日も早くやめ、今まで通り地方交付税交付金で解決したほうが良いと思う。バランスが悪いのであれば調整すれば済む話だ。それに不当な配分が行われている森林環境税といった無駄な税金を整理することで、もう少し増税も抑えることができると思う。例えば渋谷区には森林がないが森林環境税が交付されている[^10]。渋谷区はこれを単に積み立てているというが、これはタダの無駄である。渋谷区は八王子市にでも全額寄付したほうがいいだろう。
人間の存在価値
単純作業の繰り返しのような仕事について「こんなのは人間のする仕事ではない」という言葉をしばしば聞くことがある。では「人間がする仕事」とは何だろう?
先の女性の活躍や、AIの話と被るところもあるのだが、とどのつまり人間は何をするべきで、何のために存在しているのかというところである。AIが何でもしてくれるなら人間はいらないし、婦人会も女性差別だからいらない。東京以外の地方が都民の税金を食いつぶしていると言う話[^11]もあるので、この理屈でいえば地方の人間はいらない。
では、果たして必要な人間とは何か?
これは今流行りのSDGsにも繋がる話だと思っている。なぜ人は生きる必要があり、人や国の不平等をなくす必要があるのか?人や国の不平等をなくすというのは究極的には国民国家の破壊であると私は考えている。例えばその国の風俗とは別の国にはないもので、これは不平等であるといえる。では、もしこの目標が達成されたときに、人は一体何のために生きるのか。
個人的には各社の企業理念や、SDGsのようなスローガン、国際社会の動きや、日々の社会生活などを見ていると、人や国民国家、各地に伝わる風俗は何のためにあるのか、その歴史と伝統は本当に失ってよいものなのか?とか、そういうことを考ええるのである。
ここのところ旧世代への変革記事が続いているが、今回は新聞だ。敢えて地方紙である神戸新聞を取ることにした。
私はこれまでもネットニュースは見ていたのだが内容の偏りや、興味のない情報が多く悩みを抱えていた。特に私は兵庫県や神戸市の情報を重視したかったので、今年あたりから神戸新聞NEXTの無料版を利用していたのだが、読めない記事が多く困っていたので去る10月に神戸新聞NEXTの購読契約をしたのだが、どうにも情報量が少ないと感じた。主要なニュースはあるのだが、それ以外が薄いというか。
そこで思い浮かんだのが紙面購読だ。紙面であれば漏れなく全ての情報があるし、毎朝・夕届くので生活習慣をつけるのにもいい。毎朝ポストまで取りに行くのは良い習慣になるだろうというのと、紙のメディアを読むというのは新鮮な体験になるだろうと思い、まずは試読を申し込んだ。実はこれしばらく届かなかったのだが、どうも誤配があったようで一週間後からはちゃんと届くようになった。無論、誤配中の試読はなかったことになり、新しく試読を始めることができたので神戸新聞の対応には感謝したい。
そして予想通り紙の新聞には電子版には書かれていない情報が多く、また画面遷移せずとも一覧的に情報を取り込めるのは非常に良い体験だった。そして新聞記事だけでなく、読者投稿や地域の細かい話、チラシなども見ることができる。情報のチャンネルが自然に増えるというのはとても良いことだ。例えば読者が普段感じていることや、年末年始のごみ収集についても書かれている。特に年末年始のごみ収集の情報を特に意識せずとも入手できるのはとてもありがたい。チラシについてもWeb広告のような不快感をあおるようなものは今のところ届いておらず、地域のスーパーなどの広告がメインだ。胡散臭い霊感商法的なものや、週刊誌的な広告も紙面の端やチラシとして封入されているが、昔からあるものということもあり、個人的には特に目に付くこともない。
紙媒体というか、リアルなアナログ媒体あるあるだと思うのだが、何気ない情報に出会えるのも新聞の醍醐味だと感じる。先ほども書いたことだが一覧性が高いことによって適当にページをめくっているだけでも意外な情報と出会えることがある。これは現実の書店に行ったときに思いがけない本に出会えるのと同じ理屈だろう。私はこのことを現実世界の解像度の高さと呼んでいる。デジタル世界は解像度が低いのだ。画面に表示できる情報は高々知れている。例えVRだとしても現実には到底敵わない。何故なら今のVRの解像度が低いからだ。
今のインターネットには余りにも情報が氾濫しており、その中には誤った情報や、錯誤させることを狙ったような情報、攻撃的な情報も少なくなく、見ていて非常に疲れる。これは私がX(旧Twitter)から身を引き始めた理由の一つでもある。今ではTwitterへの投稿数は最盛期の一割程度にまで落ち込んでいる。普段はログインすらしておらず、アプリも消してできるだけ見ないようにしている。利用するのはもっぱらブラウザ経由で毎回ログインしてはログアウトする運用をしている。
この辺りは、なるべくSNSに依存せずにブログを書いていこうという方針にしている。SNSに張り付いていると疲れるからだ。同様に新聞も朝夕だけ見ればいい。見切れなければ適当な空き時間に読めばいいのだ。時間などいくらでもあるのだから。
最近地方紙は衰退の危機にあるともいう部分もあるし、伝統的な新聞配達というのも続いてほしい気持ちがあるので試読が終わったら定期購読に移ろうと考えている。あと新聞紙があれば態々新聞紙の代わりになる神を買わなくていいのもメリットだろう。紙が欲しくなれば、すぐそこにいくらでもある状況だ。市の広報誌も取っているので新聞紙自体のストックはあるが広報誌の数など知れているので年末の大掃除くらいでしか役に立たない。
勿論、紙の新聞を使うことで貴重な資源やエネルギーの無駄遣い、配達員への負荷が増えるなどあるだろう。新聞配達は過酷な労働だ。人権侵害である可能さえある。しかしだからと言ってなくしていいものか?私は歴史と伝統は守られたほうが良いと考えているため、一概に西洋の価値観を肯定する気はなく、日本の歴史や文化を尊び、守っていくために大切にしていきたいと思っている。
果たして紙の新聞がなくなり、新聞のコモディティ化により地方紙も消えた世界が本当にいい世界なのかどうか、それは誰にとってどういい世界なのか、そこを考える価値はあると私は思っている。地方紙が消えれば次に消えるのは全国紙だ。世界各国から新聞会社が消え、NEWYORK TIMESだけになった世界というのも、今後を考えれば十分にありうる未来の一つだろう。私はそれを良しとしない。勿論、そんな極端なことになるかどうかは今はわからないが、少なくとも地方紙が風前の灯火であるというのは事実だろうから、自分なりに支えていきたいし、自分の精神の健康や、文化的生活の糧としても利用していきたいところだと考えている。
毎回忘れるので書いておく
基本は以下の動画の通りでよいが参考程度に途中の写真も置いておく
弦はペグに向けて通し、ペグの反対側を折り曲げる
動画の通りに一回目の巻きは上を通し、以降の巻きは下を通すようにする
最終形態はこんな感じ。飛び出た弦は危ないので切ってある。別に動画のように上に持ち上げてもいいと思うが、購入時点では切ってあったのと安全上の理由もあり、私は切り落としている
余談だが椅子の上に乗せてやると作業しやすい
JestからNode.js組み込みのテストランナーに移行する時の方法や注意点をまとめたメモ
- 確認環境
- Node.jsの組み込みテストランナーとは
- Jest関数との対応リスト
describe(name, fn),it(name, fn, timeout?),test(name, fn, timeout?)beforeAll(fn, timeout?),afterAll(fn, timeout?),beforeEach(fn, timeout?),afterEach(fn, timeout?).toBe(value),.not.toBe(value).toStrictEqual(value),.not.toStrictEqual(value).toHaveLength(number),.toBeNull(),.toBeUndefined(),.toBeNaN().toBeTruthy().toBeInstanceOf(Class).toThrow,not.toThrowexpect.arrayContaining(array),expect.objectContaining(object),expect.stringContaining(string)jest.spyOn(object, methodName, accessType?).toHaveBeenCalled(),.toHaveBeenCalledTimes(number).toHaveBeenCalledWith(arg1, arg2, ...)jest.fn(implementation?)jest.useFakeTimers(fakeTimersConfig?),jest.runAllTimers()jest.useRealTimers()mockFn.mockClear()mockFn.mockReset()- モック機能についての備考
確認環境
| Env | ver |
|---|---|
| Node.js | 20.8.0 |
| Jest | 29.7 |
Node.jsの組み込みテストランナーとは
英語圏ではNode.js built-in test runnerとか呼ばれている存在で、Node.jsの組み込みテストランナーのこと。恐らくDenoのテストランナーに対抗して生まれた気配がする(Node.jsはDenoに機能追加があると真似する傾向があるため)
Node.jsの組み込みテストランナーは機能が二分されており、テストランナーとアサートに分かれている。Jestみたいにエコシステムが発達しておらず、標準ではdescribeなどはimportする必要がある。TypeScriptをテストする場合は間にレジスター[^1]を嚙ます必要がある。噛まし方はswc/jestでESM, CJS混在のコードをJestを使ってテストする有力な方法は今のところ多分ないに書いた。
Jest関数との対応リスト
describe, it, beforeAll, afterAll, beforeEach, afterEach辺りは違和感がなかったが、それ以外は軒並み互換性がないので大きく書き換えが必要だと感じた。便利なMatcherは完膚なきまでに全滅している。お陰で覚えることが減ったのは逆に良くなったと感じる
なお、not始まりの機能は実際の動作を確認しておらず、Jestと同じ機能かどうかは確認していないことに留意すること
describe(name, fn), it(name, fn, timeout?), test(name, fn, timeout?)
Node.jsのTest機能では、以下となる
describe([name][, options][, fn])it([name][, options][, fn])test([name][, options][, fn])
基本的な差はないがJestにあったtimeout引数はNode.jsではoptionsパラメーターで設定するようになっている模様。今回調べるまで存在自体を知らなかったのもあり、このtimeoutがJestと同じ機能かどうかは確認していない
import { describe, it } from 'node:test';
describe('hoge', () => {
it('hoge', () => {
// ここにテストコード
});
it.todo('piyo');
});
beforeAll(fn, timeout?), afterAll(fn, timeout?), beforeEach(fn, timeout?), afterEach(fn, timeout?)
Node.jsのTest機能では、以下となる
before([fn][, options])after([fn][, options])beforeEach([fn][, options])afterEach([fn][, options])
基本的な差はないがJestにあったtimeout引数はNode.jsではoptionsパラメーターで設定するようになっている模様。今回調べるまで存在自体を知らなかったのもあり、このtimeoutがJestと同じ機能かどうかは確認していない
+import { after, before, beforeEach, afterEach, describe } from 'node:test';
+
describe('test', () => {
- beforeAll(() => {
+ before(() => {
console.log('before');
});
beforeEach(() => {
console.log('beforeEach');
});
afterEach(() => {
console.log('afterEach');
});
- afterAll(() => {
+ after(() => {
console.log('after');
});
});
.toBe(value), .not.toBe(value)
Node.jsのTest機能では、以下となる
assert.strictEqual(actual, expected[, message])assert.notStrictEqual(actual, expected[, message])
+import { describe, it } from 'node:test';
+import assert from 'node:assert';
+
describe('test', () => {
it('test', () => {
const actual = 123;
- expect(actual).toBe(123);
+ assert.deepStrictEqual(actual, 123);
});
});
.toStrictEqual(value), .not.toStrictEqual(value)
Node.jsのTest機能では、以下となる
assert.deepStrictEqual(actual, expected[, message])assert.notDeepStrictEqual(actual, expected[, message])
+import { describe, it } from 'node:test';
+import assert from 'node:assert';
+
describe('test', () => {
it('test', () => {
const actual = {
hoge: {
piyo: {
id: 1,
value: 'one'
}
},
fuga: [[123, 456], 'ABC', 'EFG']
};
- expect(actual).toStrictEqual({
+ assert.deepStrictEqual(actual, {
hoge: {
piyo: {
id: 1,
value: 'one'
}
},
fuga: [[123, 456], 'ABC', 'EFG']
});
});
});
.toHaveLength(number), .toBeNull(), .toBeUndefined(), .toBeNaN()
Node.jsのTest機能では、assert.strictEqual()となる
+import { describe, it, mock } from 'node:test';
+import assert from 'node:assert';
+
describe('test', () => {
it('test', () => {
const arr = [1, 2, 3];
const undef = undefined;
const nil = null;
const nan = NaN;
- expect(arr).toHaveLength(3);
- expect(undef).toBeUndefined();
- expect(nil).toBeNull();
- expect(nan).toBeNaN();
+ assert.strictEqual(arr.length, 3);
+ assert.strictEqual(undef, undefined);
+ assert.strictEqual(nil, null);
+ assert.strictEqual(nan, NaN);
});
});
.toBeTruthy()
Node.jsのTest機能では、以下となる
assert.ok(value[, message])
否定版は不明
+import { describe, it, mock } from 'node:test';
+import assert from 'node:assert';
+
describe('test', () => {
it('test', () => {
- expect(true).toBeTruthy();
- expect({}).toBeTruthy();
- expect([]).toBeTruthy();
- expect(42).toBeTruthy();
- expect('0').toBeTruthy();
- expect('false').toBeTruthy();
- expect(new Date()).toBeTruthy();
- expect(-42).toBeTruthy();
- expect(12n).toBeTruthy();
- expect(3.14).toBeTruthy();
- expect(Infinity).toBeTruthy();
- expect(-Infinity).toBeTruthy();
+ assert.ok(true);
+ assert.ok({});
+ assert.ok([]);
+ assert.ok(42);
+ assert.ok('0');
+ assert.ok('false');
+ assert.ok(new Date());
+ assert.ok(-42);
+ assert.ok(12n);
+ assert.ok(3.14);
+ assert.ok(Infinity);
+ assert.ok(-Infinity);
});
});
.toBeInstanceOf(Class)
Node.jsのTest機能では、assert.ok()となる
結果がTruthyであればなんでもpassするので注意
+import { describe, it } from 'node:test';
+import assert from 'node:assert';
+
describe('test', () => {
it('test', () => {
const actual = new Error('hoge');
- expect(actual).toBeInstanceOf(Error);
+ assert.ok(actual instanceof Error);
});
});
.toThrow, not.toThrow
Node.jsのTest機能では、以下となる
assert.throws(fn[, error][, message])assert.doesNotThrow(fn[, error][, message])
Jestより便利になっており、Error型以外も扱えるので後述する
+import { describe, it, mock } from 'node:test';
+import assert from 'node:assert';
+
describe('test', () => {
it('test', () => {
const err = new Error('test');
- expect(() => {
+ assert.throws(() => {
throw err;
- }).toThrow(err);
+ }, err);
});
});
Node.jsではJestと異なりError型以外も扱える
Jestの`.toThrow()はError型以外を扱うことができず、オブジェクトをThrowするようなコードではエラーになる
describe('test', () => {
it('test', () => {
const obj = { id: 1, value: 'hoge' };
// このテストは失敗する。またTypeScriptの型エラーにもなる
expect(() => {
throw obj;
}).toThrow(obj);
// このテストは失敗する。またTypeScriptの型エラーにもなる
expect(() => {
throw obj;
}).toThrow({ id: 1, value: 'hoge' });
});
});
しかしNode.jsであればこれはエラーにならない。例えば次のテストコードは成功する
import { describe, it, mock } from 'node:test';
import assert from 'node:assert';
describe('test', () => {
it('test', () => {
const obj = { id: 1, value: 'hoge' };
assert.throws(() => {
throw obj;
}, obj);
assert.throws(
() => {
throw obj;
},
{ id: 1, value: 'hoge' }
);
});
});
expect.arrayContaining(array), expect.objectContaining(object), expect.stringContaining(string)
恐らく非対称マッチャーはないので自分でロジックを書いてassert.strictEqual()で判定するしかないと思われる。元々微妙な機能だったのでやむなし
jest.spyOn(object, methodName, accessType?)
Node.jsのTest機能では、node:testからmockをimportして以下を使う
mock.method(object, methodName[, implementation][, options])
+import { describe, it, mock } from 'node:test';
+
describe('test', () => {
it('test', () => {
- jest.spyOn(console, 'log');
+ mock.method(console, 'log');
});
});
本来の挙動を塞ぎたい場合
例えば以下のようにテストコードを書いた場合、execSync()が実際の挙動で動作してしまい、テストとして機能しない。
import { describe, it, mock } from 'node:test';
import assert from 'node:assert';
import child_process, { execSync } from 'node:child_process';
const mockedExecSync = mock.method(child_process, 'execSync');
describe('execSync', () => {
it('execSyncが正しい引数で呼ばれること', () => {
execSync('false');
assert.strictEqual(mockedExecSync.mock.calls[0].arguments[0], 'false');
});
});
このような場合、以下のようにmock.method()の第三引数を指定してやると封じることができる。単体テストの観点では基本的に第三引数には空関数を入れておくのが望ましいだろう。
import { describe, it, mock } from 'node:test';
import assert from 'node:assert';
import child_process, { execSync } from 'node:child_process';
- const mockedExecSync = mock.method(child_process, 'execSync');
+ const mockedExecSync = mock.method(child_process, 'execSync', () => {});
describe('execSync', () => {
it('execSyncが正しい引数で呼ばれること', () => {
execSync('false');
assert.strictEqual(mockedExecSync.mock.calls[0].arguments[0], 'false');
});
});
モックパターン
module.exportsされている関数のモック
import foo from 'foo';形式でmock.method()の第一引数を埋める
import { describe, it, mock } from 'node:test';
import assert from 'node:assert';
import child_process, { execSync } from 'node:child_process';
const mockedExecSync = mock.method(child_process, 'execSync', () => {});
describe('execSync', () => {
it('execSyncが正しい引数で呼ばれること', () => {
execSync('false');
assert.strictEqual(mockedExecSync.mock.calls[0].arguments[0], 'false');
});
});
上記が正常に動作することはNode.js v20.0.0時点のコードで確認している
Global objectから生えている関数のモック
以下のようにmock.method()の第一引数にGlobal objectを設定すればよい
import { describe, it, mock } from 'node:test';
import assert from 'node:assert';
describe('exit', () => {
// exitが実際に走って落ちるのでmock.methodの第三引数を指定している
const mockedExit = mock.method(process, 'exit', () => {});
it('call exit', () => {
process.exit(1);
assert.strictEqual(mockedExit.mock.calls.length, 1);
});
});
Named exportされている関数のモックは今のところ無理そう
ファイルモックをする手段がないので正攻法では無理そう
https://github.com/nodejs/help/issues/4298
2024-10-15追記
Node.js v22.3.0でテスト走行時に--experimental-test-module-mocksを渡すことで近いことができるようになった模様だが、試したところ上手く動かないし、spy的な使い方はできなさそうだ。mock.method()との組み合わせも試してみたが、上手くいかなかった。
ObjectやNamespaceでラップされている関数のモック
実装例(Object)
export const Hoge = {
validateFoo() {
// 例外を飛ばす可能性のある何かの処理
},
hoge() {
Hoge.validateFoo();
return 1;
},
};
実装例(Namespace)
export namespace Hoge {
export const validateFoo = () => {
// 例外を飛ばす可能性のある何かの処理
};
export const hoge = () => {
validateFoo();
return 1;
};
}
実装例に対するテストコード
ObjectもNamespaceも同じ書き方でテスト可能
import { describe, it } from 'node:test';
import assert from 'node:assert';
import { Hoge } from './hoge';
describe('hoge', () => {
it('validateFooが例外をスローした場合、例外がスローされること', (t) => {
t.mock.method(Hoge, 'validateFoo', () => {
throw new Error('foo');
});
assert.throws(() => {
Hoge.hoge();
}, Error('foo'));
});
it('全ての関数が正常終了した場合、戻り値を返すこと', () => {
const actual = Hoge.hoge();
assert.strictEqual(actual, 1);
});
});
.toHaveBeenCalled(), .toHaveBeenCalledTimes(number)
Node.jsのTest機能では、assert.strictEqual()でモックから生えてるやつを調べる。returnも同様の手法で実現できる
+import { describe, it, mock } from 'node:test';
+import assert from 'node:assert';
+
describe('test', () => {
it('test', () => {
- const spiedConsoleLog = jest.spyOn(console, 'log');
+ const mockedConsoleLog = mock.method(console, 'log');
console.log();
- expect(spiedConsoleLog).toHaveBeenCalled();
- expect(spiedConsoleLog).toHaveBeenCalledTimes(1);
+ assert.deepStrictEqual(mockedConsoleLog.mock.calls.length, 1);
});
});
.toHaveBeenCalledWith(arg1, arg2, ...)
Node.jsのTest機能では、assert.strictEqual()でモックから生えてるやつを調べる。returnも同様の手法で実現できる。
Jestでは.toEqual()処理されるがNode.jsの組み込みテストランナーの場合、厳密比較ができるので便利
+import { describe, it, mock } from 'node:test';
+import assert from 'node:assert';
+
describe('test', () => {
it('test', () => {
- const spiedConsoleLog = jest.spyOn(console, 'log');
+ const mockedInfo = mock.method(console, 'log');
console.log('test');
- expect(spiedConsoleLog).toHaveBeenCalledWith('test');
+ assert.deepStrictEqual(mockedInfo.mock.calls[0].arguments[0], 'test');
});
});
jest.fn(implementation?)
Node.jsのTest機能では、以下となる
mock.fn([original[, implementation]][, options])
+import { describe, it, mock } from 'node:test';
+import assert from 'node:assert';
+
+// 動作確認用の関数
const testTarget = (cbParam: string, callbackFn: (param: string) => number) => {
return callbackFn(cbParam);
};
describe('test', () => {
it('test', () => {
- const mockFn = jest.fn((_: string) => {
+ const mockFn = mock.fn((_: string) => {
return 5;
});
const actual = testTarget('hoge', mockFn);
- expect(mockFn).toBeCalledWith('hoge');
- expect(mockFn).toReturnWith(5);
- expect(actual).toBe(5);
+ assert.deepStrictEqual(mockFn.mock.calls[0].arguments[0], 'hoge');
+ assert.deepStrictEqual(mockFn.mock.calls[0].result, 5);
+ assert.deepStrictEqual(actual, 5);
});
});
jest.useFakeTimers(fakeTimersConfig?), jest.runAllTimers()
Node.jsのTest機能では、以下となる
mock.timers.enable([timers])mock.timers.runAll()
+import { describe, it, mock } from 'node:test';
+import assert from 'node:assert';
+
+describe('test', () => {
- jest.useFakeTimers();
+ mock.timers.enable();
+ it('test', () => {
- const mockFn = jest.fn();
+ const mockFn = mock.fn();
setTimeout(() => {
mockFn();
}, 9999);
- expect(mockFn).not.toHaveBeenCalled();
- jest.runAllTimers();
- expect(mockFn).toHaveBeenCalledTimes(1);
+ assert.deepStrictEqual(mockFn.mock.calls.length, 0);
+ mock.timers.runAll();
+ assert.deepStrictEqual(mockFn.mock.calls.length, 1);
});
});
jest.useRealTimers()
Node.jsのTest機能では、以下となる
mock.timers.reset()
+import { mock } from 'node:test';
-jest.useRealTimers();
+mock.timers.reset();
mockFn.mockClear()
Node.jsのTest機能では、以下となる
ctx.resetCalls()
+import { describe, it, mock } from 'node:test';
+import assert from 'node:assert';
+
describe('test', () => {
it('test', () => {
- const mockFn = jest.fn((param: string) => `<${param}>`);
+ const mockFn = mock.fn((param: string) => `<${param}>`);
mockFn('hoge');
- expect(mockFn).toHaveBeenCalledTimes(1);
- expect(mockFn).toReturnWith('<hoge>');
+ assert.deepStrictEqual(mockFn.mock.calls.length, 1);
+ assert.deepStrictEqual(mockFn.mock.calls[0].result, '<hoge>');
- mockFn.mockClear();
+ mockFn.mock.resetCalls();
- expect(mockFn).toHaveBeenCalledTimes(0);
+ assert.deepStrictEqual(mockFn.mock.calls.length, 0);
mockFn('piyo');
- expect(mockFn).toHaveBeenCalledTimes(1);
- expect(mockFn).toReturnWith('<piyo>');
+ assert.deepStrictEqual(mockFn.mock.calls.length, 1);
+ assert.deepStrictEqual(mockFn.mock.calls[0].result, '<piyo>');
});
});
mockFn.mockReset()
Node.jsのTest機能では、以下となる
mockFn.mock.restore()
振る舞いが微妙に違うため後述する
+import { describe, it, mock } from 'node:test';
+import assert from 'node:assert';
+
describe('test', () => {
it('test', () => {
- const spiedConsoleLog = jest
- .spyOn(console, 'log')
- .mockImplementation((param: any) => {
- return `<${param}>`;
- });
+ const mockedConsoleLog = mock.method(console, 'log', (param: any) => {
+ return `<${param}>`;
+ });
console.log('hoge');
- expect(spiedConsoleLog).toHaveBeenCalledTimes(1);
- expect(spiedConsoleLog).toReturnWith('<hoge>');
+ assert.deepStrictEqual(mockedConsoleLog.mock.calls.length, 1);
+ assert.deepStrictEqual(mockedConsoleLog.mock.calls[0].result, '<hoge>');
- spiedConsoleLog.mockReset();
+ mockedConsoleLog.mock.restore();
});
});
JestとNode.jsでの振る舞いの差異
但しJestとNode.jsのTest機能では微妙に差異がある
例えばJestでは以下の実装が正しくPASSするが
describe('test', () => {
it('test', () => {
const mockFn = jest
.spyOn(console, 'log')
.mockImplementation((param: any) => {
return `<${param}>`;
});
console.log('hoge');
expect(mockFn).toHaveBeenCalledTimes(1);
expect(mockFn).toReturnWith('<hoge>');
mockFn.mockReset();
expect(mockFn).toHaveBeenCalledTimes(0);
console.log('piyo');
expect(mockFn).toHaveBeenCalledTimes(1);
expect(mockFn).not.toHaveBeenCalledWith();
expect(mockFn).toReturnWith(undefined);
});
});
Node.jsで以下の実装を書いても同じように機能しない
import { describe, it, mock } from 'node:test';
import assert from 'node:assert';
describe('test', () => {
it('test', () => {
const mockFn = mock.method(console, 'log', (param: any) => {
return `<${param}>`;
});
console.log('hoge');
assert.deepStrictEqual(mockFn.mock.calls.length, 1);
assert.deepStrictEqual(mockFn.mock.calls[0].result, '<hoge>');
mockFn.mock.resetCalls();
mockFn.mock.restore();
assert.deepStrictEqual(mockFn.mock.calls.length, 0);
console.log('piyo');
// 以降のテストはいずれも落ちる
assert.deepStrictEqual(mockFn.mock.calls.length, 1);
assert.deepStrictEqual(mockFn.mock.calls[0].result, '<piyo>');
});
});
但しモック実装を削除したうえで再度呼び出すという行為には意味がないので、特に問題にはならないと思われる
モック機能についての備考
it([name][, options][, fn])の第三引数のコールバックの第一引数にはTestContextが入っており、これを使ってモックすることもできる
これを使う場合、スコープアウトでモックが復元されるため、例えば以下のような関数をテストするときに便利である。
実装
export namespace Hoge {
export const validateFoo = () => {
// 例外を飛ばす可能性のある何かの処理
};
export const validateBar = () => {
// 例外を飛ばす可能性のある何かの処理
};
export const validateBaz = () => {
// 例外を飛ばす可能性のある何かの処理
};
export const hoge = () => {
validateFoo();
validateBar();
validateBaz();
return 1;
};
}
テストコード
import { describe, it } from 'node:test';
import assert from 'node:assert';
import { Hoge } from './hoge';
describe('hoge', () => {
it('validateFooが例外をスローした場合、例外がスローされること', (t) => {
t.mock.method(Hoge, 'validateFoo', () => {
throw new Error('foo');
});
assert.throws(() => {
Hoge.hoge();
}, Error('foo'));
});
it('validateBarが例外をスローした場合、例外がスローされること', (t) => {
t.mock.method(Hoge, 'validateBar', () => {
throw new Error('bar');
});
assert.throws(() => {
Hoge.hoge();
}, Error('bar'));
});
it('validateBazが例外をスローした場合、例外がスローされること', (t) => {
t.mock.method(Hoge, 'validateBaz', () => {
throw new Error('baz');
});
assert.throws(() => {
Hoge.hoge();
}, Error('baz'));
});
it('全ての関数が正常終了した場合、戻り値を返すこと', () => {
const actual = Hoge.hoge();
assert.strictEqual(actual, 1);
});
});





