コーヒー飲みながら仕事したい

仕事で使う技術的なことの備忘録とか

moq で virtual メソッドをコールする方法

moq を使っていて、ハマったことがあったので備忘録として残しておきます。
主題の通り、「moq で virtual メソッドをコールする方法」です。

やりたかったことと症状

以下のような Dispose パターンを使用したテスト対象のクラスがあったとします。

public class abstract BaseClass : IDisposable
{
    protected virtual void Dispose(bool disposing)
    {
        if(disposing) DisposeCore();
    }

    abstract void DisposeCore();

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

DisposeCore メソッドをモックして、Dispose() 実行時にモックした DisposeCore が呼ばれることをテストしようとした際、virtual な Dispose(bool) がコールされる必要があります。

おそらくコードとしては以下の感じでしょうか。

[Fact]
public void DisposeTest()
{
    var mock = new Mock<BaseClass>();

    mock.Protected().Setup("Dispose", ItExpr.IsAny<bool>()).Callback(()=> /*何かのテスト処理*/);

    mock.Object.Dispose();
}

しかし、このままでは Dispose() 内部でコールされるはずの virtual Dispose(bool) が実行されないのです。

原因と解決方法

原因は、デフォルトでは moq は virtual メソッドは実行されないからっぽいです。

ここまでは、実際の動作からしてそうなんだろうと予想はつくのですが、いかんせん対策方法がわかりません。
で、検索すると、英語版の StackOverflow にまんま答えがありました。

stackoverflow.com

以下の通りフラグを立ててやればよいらしいです。

[Fact]
public void DisposeTest()
{
    var mock = new Mock<BaseClass>();

    mock.Protected().Setup("Dispose", ItExpr.IsAny<bool>()).Callback(()=> /*何かのテスト処理*/);
    mock.CallBase = true; // <- これ!!

    mock.Object.Dispose();
}

実際にこれで、virtual Dispose(bool) がコールされました。

消せないファイルを消したい!ときの IObit Unlocker

主題の通り、Windows にてファイルを消そうとすると、「プロセスがどうたら~」とか余計なおせっかいを言われてファイルが消せないことが多々あります。
「ええから消させてくれい!」というときに結構困るんですが、そんなとき用の便利ツールが以下の「IObit Unlocker」です。

jp.iobit.com

何が良いかって、シンプルな UI でサクッと目的を達成できるのでいいですね。
おすすめです。

mosquitto のバグ(たぶん)について

mosquitto は mqtt ブローカーの参照実装と言われるだけあって、安定していて使いやすいのですが、やはりバグがいくつかあります。(現状 Github で報告されている issue だけでもこれだけあります)

今回、私のプロジェクトにおいて関係するバグを備忘録として残しておきます。

Windows 環境において、 mosquitto_loop_stop(mosq, false) が正常に動作しない

github.com

期待される動作

mosquitto_loop_stop 関数は、ブローカーとの通信スレッドを停止する API ですが、第2引数 (bool force) の値によって以下のように動作が異なるという仕様です。

  • false の場合
    ブローカーとの通信終了における正常な手順である mosquitto_disconnect 関数をあらかじめコールしてからでないと、スレッドが終了しない。つまり、 mosquitto_disconnect 関数をコールせずに mosquitto_loop_stop(mosq, false) しても、制御が戻らない
  • true の場合
    mosquitto_disconnect 関数をコールしていないくても、強制的に通信スレッドを停止させる。ただし、この場合はブローカーはクライアントと異常切断したと判断する。(なので、 will が実行される)

バグの症状

ここで、 Windows 環境におけるバグですが、第2引数を true としても、強制的に通信スレッドを停止しません。つまり、第2引数が常に false として動作するということです。

一応対応策は上記 issue にて提案はされているのですが、既に2年ほど放置されています・・・

やはり OSS なので、困っている人が自分で解決してコントリビュートするべきですかねぇ

Git Bash on Windows で gitlab に ssh 接続するときにハマったこと

事象

社内のオンプレミスの gitlab に対して、 tortoise git では ssh できるのに、 git bash では ssh できない!1

原因

普段 ssh で使用している秘密鍵は、 putty で作成した鍵でした。 tortoise git 使っているときには、putty 形式の鍵が使えたのですが、 git bash では対応していないみたいです。

qiita.com

その代わり、 git bash 上で ssh-keygen コマンドで生成したものを使うとうまくいくみたいです。

対策方法

考えうる方法としては以下があります。

  1. putty秘密鍵を、open-ssh 形式に変更する。([bash]puttyで作成した公開鍵をopenssh(GitHubなど)キーへ変換する方法 | Coffee Breakにプログラミング備忘録)
  2. tortoise git 用と gitbash 用で鍵を使い分ける(つまり、gitlab に2通りの鍵を登録する)

本来は 1. のほうがスマートなのですが、上記リンクにあるやり方を実行しても、 uudecode failed. って言われて拒否されたので、今回は 2. のやり方を採用します。

本題

ということで、本題の git bash で gitlab に ssh する方法についてです。以下、備忘録として手順を載せておきます。

鍵を生成する

いろいろ調べてみたんですが、なかなかすんなりうまくいきませんでした。結局何が正解かというと、やっぱり本家なんですよね。
ということで、英語だったので敬遠してたのですが、 gitlab 公式を参考にします。

docs.gitlab.com

ssh-keygen -t rsa -C "your.email@example.com" -b 4096

すると、出力ファイル名を聞かれるので、適当に入力します。

Generating public/private rsa key pair.
Enter file in which to save the key (XXX/.ssh/id_rsa):

デフォルトで id_rsa っぽいので、ここはそのまま enter しました。

Enter passphrase (empty for no passphrase):
Enter same passphrase again:

言われるまま任意のパスワードを入力します。
これで、鍵が生成されました。

gitlab に公開鍵を登録する

生成されたファイルの一つである id_rsa.pub をメモ帳とかで開き、中身を全てコピーします。
で、[gitlab の画面の右上にある自分のアイコン] -> [Settings] -> [SSH Keys] で、「Key」欄にコピーした内容を貼り付けます。(「Title」欄はコピペしたときに勝手に補完されるみたい)で、「Add key」で登録できます。

config ファイルへの登録

ここが一番のミソです。というか、ここが一番ハマりました。(ググってもあまり情報が出てこなかった・・・)
どうやら git bash では、~/.ssh の config ファイルを使用せず、独自の config を使用するみたいです。

qiita.com

ということで、C:\Program Files\Git\etc\ssh フォルダ内にある ssh_config ファイルを開きます。

試行錯誤の上、私の環境では以下のような設定を追加しました。

Host gitlab
  User git
  HostName 172.XX.YY.ZZ    ←IPアドレス
  IdentityFile ~/.ssh/id_rsa
  Port XXXXX ←ポート
  IdentitiesOnly yes

そして、以下の通り接続できることを確認します。

$ ssh -T gitlab
Enter passphrase for key '/c/XXXX/.ssh/id_rsa':
Welcome to GitLab, 〇〇〇!

これでとりあえず git bash から gitlab へアクセスできることが確認できました。

まとめ

上記手順で設定すると、 git subtree で gitlab にアクセスすることができるようになりました。
(しかし、何故かいちいちパスフレーズを聞いてくるのがちょっと面倒です・・・)

まあでも動くようになったので、良しとしましょう・・・


  1. 普段、社内ではリモートリポジトリとして、オンプレミスの gitlab を使用しています。そしてクライアント側(というか私の作業環境)では、 tortoise git を使用して gitlab にアクセスしています。現在のプロジェクトでは git subtree を使用しているのですが、残念なことに tortoise git は subtree に対応していません。仕方がないので、 subtree 使う時だけ git bash を使用しているのですが、そのときに現象に気づきました。

Observer パターンを自分なりに整理してみる

免責

いきなり免責というのもアレですが、この記事の真偽は一切保証を致しかねます。
正直、かなり怪しいと思いますので、少なくとも情報収集されている方は、この記事はスキップされたほうが無難かと思われます。 (私のアカウントに書いてあることはもともと正しさの保証は一切できないのですが^^;)

目的

GoFデザインパターンの中で有名度トップ5には絶対入っているであろう「Observer パターン」ですが、そこに出てくる登場人物が個人的にはとってもわかりにくい!

やっていることは「何かが起きた時に、その何かを他の誰かへ通知する」というデザインパターンの中でもシンプルなほうなので、実装時にもよく使うんですが。
しかし、登場人物というか、その登場人物の呼び方がシチュエーションによってコロコロ変わるのが本当に厄介です。
例えば、GoF では Subject/Observer だったり、別名 Pub/Sub 方式と呼ばれ Publisher/Subscriber だったり、Rx では IObservable/IObserver だったり・・・

ということで、個人的な備忘録として GoF でいう Obesrver は、 Pub/Sub 方式では?Rx では?というのを書いてみます。

それぞれの比較

GoF の Observer パターン

まず、GoFデザインパターンで出てくるやつです。

「イベントを発行する人(= 発火元)」が Subject です。
「イベントを受信して処理する人」が、 Observer です。

上記の場合だと、シーケンス的には以下の感じになるでしょうか。

  1. あらかじめ、 Subject は、 Observer を登録しておく
    Subject#AddObserver の実行)
  2. Subject にて何かイベントが発生した場合は、 Observer へ通知する
    Subject#NotifyObservers の実行 ⇒ 内部では Observer#Update の実行)
  3. ObserverUpdate を受けて、何かしらのイベント処理を行う

言ってしまえば、Subject は観察対象、Observer は観察者ですよね。そのまんまですが。

Pub/Sub パターン

次に、GoF の Observer パターンはよく「別名 Pub/Sub パターンとも呼ばれる」と言われていますが、 Pub/Sub パターンでは Observer パターンの登場人物がどのように割り当てられるのか整理してみます。

と、その前にそもそも、「Observer パターン = Pub/Sub パターン」というのは厳密に言うと誤りらしいです。
基本的な考え方は同じなんですが、 Pub/Sub パターンでは、いわゆる Publisher と Subscriber の間に「Broker」なるものが介在します。この辺はこちらに詳しく書かれているので、省略します。

が、ここでは無理やり登場人物をあてはめます。

「イベントを発行する人(= 発行者)」が Publisher です。
「イベントを受信して処理する人(=購読者)」が、 Subscriber です。
「イベントをとりまとめる人(=仲介人)」が、 Broker です。とりまとめると言うとあやふやなので、「メッセージを受け取ると、購読登録者へ配達する人」みたいな感じでしょうか。

シーケンス的には以下になると思います。

  1. あらかじめ Subscriber は購読したいイベントの topic(イベント名みたいなもの)を、Broker に登録しておく
    Broker#Subscribe の実行)
  2. Publisher は何らかのイベントが発生した場合は、 topic と共に Broker へ通知する
    Broker#Publish の実行)
  3. Broker は、あらかじめ登録されている Subscriber に対してイベントを通知する
  4. Subscriber はイベントを受け取ると、何かしらのイベント処理を行う

つまり、Pub/Sub パターンを Observer パターンに当てはめた場合、以下のような対応になります。
イベント発生側:Publisher = Subjectイベント受信側:Subscriber = Observer

※ ただし、やはり Observer パターンとは異なり、PublisherSubscriber は基本的に依存関係にありません(つまり疎結合)。Publisher は自身が通知したイベントがどの Subscriber が購読しているか知りませんし、 Subscriber もイベントの発信元がどの Publisher なのかを知りません。

Rx (Reactive Extensions)

はっきりいって、 Rx はまったくの初心者というか勉強中の身なので偉そうなことは書けませんが、今自分が把握していること(というか理解したつもりでいること)を書きます。

もうまんまなのですが、 GoF の Observer パターンでいうと、IObserveable = Subject で、 IObserber = Observer ですね。(この時点で、なぜ名前を統一しないのか!GoF の定義をそのまま使ってくれ!と思っちゃいます1

そして、さらに恨み節ですが、「IObservableSubscribe メソッドを持つ」というのが個人的にわかりにくい。こんがらがる原因は Observer パターンが Pub/Sub パターンと関連しているという前提認識があるからなのだと思いますが、本来 IObserber が購読者であるはずなのに、 IObservableSubscribe するというのが混乱の元になってます。2

また、もっとやっかいなことに Rx では Subject という「 IObservableIObserver の両方を実装した、一人二役する」クラスがいます。
「おーい、IObservable = Subject やなかったんかーい」って、もうこの段階でパニックです。

まとめ

ということで、 Rx やるうえで、以下のことを覚えておきます。

パターン名 イベントの発行元(= 発行者)の呼び方 イベントの受信先(= 購読者)の呼び方 備考
Observer パターン Subject Observer
Pub/Sub パターン Publisher Subscriber
Rx IObservable IObserver ※ただし、IObservableIObserver の両方を実装する Subject というクラスがある

うーん、ややこしい・・・


  1. 一応補足しておくと、たぶん IEnumerableIEnumerator の絡みという理由があるのだろうと思ってます。

  2. Pub/Sub パターンでも、もし Broker が介在しなければ、 Publisher が Subscribe メソッドを持つことになるので、よく考えたらおかしくないのですが。

Visual Studio 2017 で欠かせないプラグイン 4+1つ

個人的にこれは欠かせないっていう Visual Studioプラグインを備忘録として残しておきます。

Format document on Save

Ctrl + S で保存時、自動整形を実行してから保存してくれるプラグインです。
Visual Studio 自体にもプラグインの機能があるのですが、わざわざ Ctrl + K, Ctrl + D と2回も実行しないといけないし、明示的な操作をしないとソースファイルに反映されないので、このプラグインを使うことで使い勝手が格段に向上します。

marketplace.visualstudio.com

Git Diff Margin

かなり有名なプラグインですが、やはりこれは便利です。
git 管理が前提となりますが、ソースファイルを開いていたらプラグインが自動で git diff を実行してソース上にコミット前との変更箇所が視覚的に表示されます。
なんといっても、変更前と変更後のソースを直感的に確認できるのは便利です。

marketplace.visualstudio.com

VSColorOutput

出力ウィンドウ(ビルド実行時とかにビルドログとかが表示されるところ)の文字に色を付けてくれます。
エラーだったら赤色とか、正常ビルドできたら緑色とか、地味に便利です。

marketplace.visualstudio.com

Shrink Empty Lines

このプラグインを導入すると、文字や数値を含まない行の縦幅が 25% 縮小されます。ちょっとだけですが、見通しが良くなります。

marketplace.visualstudio.com

ForceUTF8 (with BOM)

ちょっとマニアックですが、保存時の文字コードを UTF8 に自動変換してくれます。
Visual Studio のデフォルト文字コードが UTF8 なので、基本的には恩恵を受けることはないのですが、私の環境で以前 Visual Studio が自動生成したコードが Shift-JIS だったことがありました。そのせいでコンパイルが通らなくなり、その理由もなかなか判明せずにハマったので、それ以降保険として適用しています。

marketplace.visualstudio.com

Visual Studio でビルド時に「のプロジェクト情報が見つかりません。これは、プロジェクト参照がないことを示している可能性があります。」でエラーになったときの対処法

(備考:もしかしたら .NET Standard のみの事象かもしれません)

事象

主題の通り、
Visual Studio でビルド実行時に、以下のようなエラーになりました。
xxx.csproj はソリューション内で参照しているプロジェクト)

'xxx.csproj' のプロジェクト情報が見つかりません。これは、プロジェクト参照がないことを示している可能性があります。

また、出力ウィンドウには以下のようなエラーが表示されました。

4>C:\Program Files (x86)\dotnet\sdk\2.0.0\Sdks\Microsoft.NET.Sdk\build\Microsoft.NET.Sdk.targets(114,5): error : 'xxx.csproj' のプロジェクト情報が見つかりません。これは、プロジェクト参照がないことを示している可能性があります。
4>プロジェクト "zzz.csproj" のビルドが終了しました -- 失敗。

対策

どうやら、参照しているプロジェクトが本当は小文字なのに、sln ファイル内では大文字として記述してあったのが原因のようでした。
上記の場合だと、 xxx.csproj のファイル名を、 XXX.csproj (つまり大文字)に修正したら治りました。

Windows は大文字と小文字を区別しないので、適当に変更したことがダメだったようです。

日本語でググってもなかなかエラーを特定できなかったので備忘録として残しておきます。

英語でググったら出てきた解決策が以下です。

VS 2017 RC error: Cannot find project info for... This can indicate a missing project reference · Issue #5144 · dotnet/cli · GitHub