shimapapa.io

.NET,VB,C#,AzureなどMS関連中心の技術ブログ

【C# / ASP.NET WebForm】HttpRequestBase を継承しMock クラスを作成し、HttpRequest をテストする

前置き

現場の案件で、外部APIやDB接続が絡んだ少々複雑な判定でCookie を作成するという処理を実装することになり、
対象の処理でユニットテストを作成しておきたくなりました。

Cookie の作成や、作成した値を検証するには当然ですがHttpRequestHttpResponseインスタンスが必要ですが、
これらのクラスのインスタンスユニットテストで生成するのは困難(ほぼ不可能のはず)です。

そこで、ASP.NET ではHttpRequestWrapperというクラスが実装されています。
HttpRequestWrapperHttpRequestBaseという抽象クラスから派生されています。
HttpRequestBaseHttpRequestと同じプロパティ、関数を保有したクラスです。
対象となる処理の実装ではHttpRequestBaseを使用し、
HttpRequestWrapperHttpRequestをラップしたインスタンスを使用することにします。
これらはHttpResponseでも同様です。
文章だとややこしいので、サンプルコードを実装してみました。

Controller(HttpRequestBase, HttpResponseBaseを使用する処理)

public class IndexController
    {
        public IndexController()
        {

        }

        public void createCookie(HttpRequestBase request, HttpResponseBase response)
        {
            if (request.QueryString == null)
            {
                throw new ArgumentNullException();
            }
            string value = request.QueryString["hoge"];

            var cookie = new HttpCookie("hoge");
            cookie.Value = "hoge" + value;
            response.Cookies.Add(cookie);
        }

    }

aspx.cs

 public partial class Index : System.Web.UI.Page
    {
        private readonly IndexController  _controller = new IndexController();

        protected void Page_Load(object sender, EventArgs e)
        {
            _controller.createCookie(new HttpRequestWrapper(Request),
                                                     new HttpResponseWrapper(Response));
        }
    }

そして、ユニットテストコードでは以下のようにHttpRequestBaseから派生したMockクラスを作成し、
テストで必要となるプロパティや関数をOverrideして実装します。

Mockクラス

 public class HttpRequestMock : HttpRequestBase
        {
            private readonly NameValueCollection _queryString;
            public HttpRequestMock(NameValueCollection queryString)
            {
                _queryString = queryString;
            }

            public override NameValueCollection QueryString => _queryString;
        }

        public class HttpResponseMock : HttpResponseBase
        {
            private readonly HttpCookieCollection _cookies = new HttpCookieCollection();
            public override HttpCookieCollection Cookies => _cookies;
        }

テストコード

[TestMethod]
        public void createCookieNormalCase()
        {
            // Arrange
            var requestMock = new HttpRequestMock(new NameValueCollection() { { "hoge", "12345" } });
            var responseMock = new HttpResponseMock();
            var sut = new IndexController();

            // Act
            sut.createCookie(requestMock, responseMock);

            // Assert
            Assert.AreEqual("hoge12345", responseMock.Cookies["hoge"].Value);
        }

サンプルコード

今回の記事で作成したサンプルコードをGitHubにアップしました。

github.com

【Azure】「az webapp up」コマンドでエラー「The scale operation is not allowed for this subscription in this region. Try selecting different region or scale option.」

Microsoft Leanの以下の演習内で引っかかった箇所があったのでメモ。

docs.microsoft.com

AppService内に.NET Coreアプリをデプロイするコマンド

az webapp up \
    --resource-group Learn-XXXX-XXXX-... \
    --sku F1 \
    --name $WEBAPPNAME

上記を実行すると、以下のエラーが発生。

The scale operation is not allowed for this subscription in this region. 
Try selecting different region or scale option.

どうも、デフォルトで設定されているリージョンではデプロイできない権限設定になっているようだったので、
--locationオプションで許可されていそうなリージョンを設定してみてコマンド実行。

az webapp up \
    --resource-group Learn-XXXX-XXXX-... \
    --sku F1 \
    --name $WEBAPPNAME \
    --location eastus

無事デプロイされました。

【VB.NET】「区分オブジェクト」のようなクラスを作れないか試行錯誤

前回の「区分オブジェクト」に関する投稿に引き続いて関連した投稿を。

rikupapa-shima.hatenablog.com

列挙型の区分ごとにプロパティを持たせる事ができないかなと思い、
実装してみたのが以下のような抽象クラスと具象クラスを使う形式。

Public MustInherit Class Perfume

    Public ReadOnly Property myFavorite As Boolean
    Sub New(myFavorite As Boolean)
        Me.myFavorite = myFavorite
    End Sub

    Public Class Nocchi
        Inherits Perfume

        Sub New(myFavorite As Boolean)
            MyBase.New(myFavorite)
        End Sub

    End Class

    Public Class Kashiyuka
        Inherits Perfume

        Sub New(myFavorite As Boolean)
            MyBase.New(myFavorite)
        End Sub

    End Class

    Public Class AaChan
        Inherits Perfume

        Sub New(myFavorite As Boolean)
            MyBase.New(myFavorite)
        End Sub

    End Class

End Class

クラス名からインスタンスを生成するにはActivator.CreateInstance
区分の判定はGetTypeで列挙型と同様に行えます。

Module Module1

    Sub Main()

        Dim myPerfume = createMyFavoriteMember("Nocchi")

        Select Case myPerfume.GetType()
            Case GetType(Perfume.Nocchi)
                ' ...
            Case GetType(Perfume.Kashiyuka)
                ' ...
            Case GetType(Perfume.AaChan)
                ' ...
        End Select
    End Sub

    Private Function createMyFavoriteMember(memberName As String) As Perfume
        Return CType(Activator.CreateInstance(
                                    Type.GetType("Enumeration.Perfume+" & memberName),
                                    New Object() {True}), Perfume)
    End Function
End Module

実行すると以下のようになります。

f:id:rikupapa-shima:20191010231631p:plain

うーん・・・具象クラスのコンストラクタを全部書く必要があるのと、
Activator.CreateInstanceでType名をセットする部分があまりイケてない感じがする・・・

参考

aerodynamik.hatenablog.com

Git For Windowsでメッセージ「Warning: 'C:\ProgramData/Git/config' has a dubious owner: 'XXXXX'.」が表示された

背景

現場で新しいPC(離任された方が使用していたPCを引き継ぎ)に変更になったので、Git For Windowsをインストールした。
Git For Windowsのバージョンは「2.23.0」。

現象

Gitコマンドを実行したところ、以下のようなエラーメッセージが表示された。

Warning: 'C:\ProgramData/Git/config' has a dubious owner: '(XXXXX)'.
For security reasons, it is therefore ignored.
To fix this, please transfer ownership to an admininstrator.

自分の場合、「XXXXX」がこのPCを前に使用していた方のユーザーアカウントだった。
現場のポリシーとして、PCの使用者を変更する場合は、クリーンインストールせず引き継ぎ。
よって、今回はGit For Windowsは前任者の方がインストールしていた状態だったところに、
上書きインストールしたような形になった。

解決策

メッセージの指示通り、「C:\ProgramData/Git/config」のプロパティから所有者を変更しようとしたが上手く行かず、
configファイルを自作して「C:\ProgramData/Git/config」に配置した。

参考

stackoverflow.com

VB.NETでJavaの「列挙型クラス」を模倣して「区分オブジェクト」を作る

増田本、読了しました。
Amazonのレビュー見ると結構賛否両論のようですが、
オブジェクト指向設計ドメイン駆動設計の基礎固めとして最適では、と自分は感じました。

「CHAPTER2 場合分けのロジックを整理する > Javaの列挙型を使えばもっとかんたん」を読んで、
おお、JavaEnumの値ごとにコンストラクタを付与したりできるのか・・・便利・・・
と恥ずかしながら初めて知りました。

.NETでも似たようなことをできないなかな、と探したら以下のようなMS公式リファレンスがありました。

docs.microsoft.com

上記と増田本のサンプルコードを真似してVB.NETで書いてみたのが以下のコード
(YenをValueObject化するのは端折ってIntegerにしてます)

本体

Public Interface Fee
    Function yen() As Integer
    Function label() As String
End Interface

Public Class AdultFee
    Implements Fee

    Public Function yen() As Integer Implements Fee.yen
        Return 100
    End Function

    Public Function label() As String Implements Fee.label
        Return "大人"
    End Function
End Class

Public Class ChildFee
    Implements Fee

    Public Function yen() As Integer Implements Fee.yen
        Return 50
    End Function

    Public Function label() As String Implements Fee.label
        Return "子供"
    End Function
End Class

Public MustInherit Class Enumeration
    Public Shared Function valueOf(Of T)(typeName As String) As T
        Return CType(GetType(T).GetField(typeName, Reflection.BindingFlags.Public Or Reflection.BindingFlags.Static Or Reflection.BindingFlags.DeclaredOnly).GetValue(Nothing), T)
    End Function
End Class

Public Class FeeType
    Inherits Enumeration

    Public Shared Adult As New FeeType(New AdultFee)
    Public Shared Child As New FeeType(New ChildFee)

    Private ReadOnly fee As Fee

    Sub New(fee As Fee)
        Me.fee = fee
    End Sub

    Public Function yen() As Integer
        Return fee.yen
    End Function

    Public Function label() As String
        Return fee.label
    End Function

End Class

クライアント側

Module Module1

    Sub Main()

        Dim feeForAdult = feeFor("Adult")
        Dim feeForChild = feeFor("Child")

    End Sub

    Public Function feeFor(feeTypeName As String) As Integer
        Dim theFeeType = FeeType.valueOf(Of FeeType)(feeTypeName)
        Return theFeeType.yen
    End Function

End Module

実行してみましょう。

f:id:rikupapa-shima:20190927230703p:plain

値取れてますね。
良い感じですが、リフレクションで変数名を指定するのはちょっと怖いです。以上。

【読了】「Adaptive Code ~ C#実践開発手法 第2版」

Adaptive Code ~ C#実践開発手法 第2版 (マイクロソフト関連書)

Adaptive Code ~ C#実践開発手法 第2版 (マイクロソフト関連書)

「Adaptive Code ~ C#実践開発手法 第2版」を読了しました。

学び

第3章 依存関係と階層化

3.3.2 横断的関心事

横断的関心事(ロギングやトランザクション管理など)は、
カプセル化した上でコードに適用するのが悪影響の少ない方法。
アスペクト指向プログラミングの導入→C#AORライブラリとして「PostSharpがおすすめ。」

www.postsharp.net

第5章 テスト

5.1.3 より複雑なテスト

モックフレームワークMoq2の利用。
下位のレイヤーでスローされた例外を上位の新しい例外でラッピングすることが推奨される。

5.2.1 保守可能なテストの書き方

SUT(System Under the Test)を表す変数にはsutという名前をつけるのには一貫性の利点がある。
テストメソッドの名前は実装ではなく意図が伝わるようにする。

5.3 テストのBuilderパターン

Builderパターンを利用し、モッククラスのメソッドを数珠つなぎに出来るようにすることで、
テストの意図を明確にすることが出来る。
テストの循環的複雑度は1を超えることがないようにする。

第12章 依存性注入

App.configファイルのセクションを使って、インターフェイスを実装にマッピングする方法を示すことができる。
(.NetFramework4.7.2より、ASP.Net WebFormsなどでもDependency Injectionが行えるようになったが、
web.configなどで実装を定義できるかが不明だったが、この方法で実装できる?)

感想

第1部ではスクラム、カンバンについての紹介にページが割かれているが、
第2部以降ではコード例なども交えて実践的な開発手法についての解説に入っていく。

などなど多岐にわたるプラクティス・原則について、実践的なコード例を多数交えて紹介している。

コード例も、参考書等でよくある説明のために簡素化された形ではなく、タイトルどおり「実践的」であると言える。 前述した「テストのBuilderパターン」などは早速現場で利用し始めている。

かなりのボリュームではあるが、オブジェクト指向の初心者/中級者が上級者にステップアップしていくために
必ず役に立つ1冊であると断言できる。

Amazonのレビューが2件しかないのが本当に不思議。

【Azure】AZ-900:Microsoft Azure Fundamentals に合格しました

今月頭にAZ-900:Microsoft Azure Fundamentalsを受験し合格しました。

www.microsoft.com

勉強方法としては、まずMicrosoft Learnの以下のラーニングパスを読み込みました。

docs.microsoft.com

さらに自分は心配だったため、以下のUdemyのコースを購入しました。

AZ-900 -- Microsoft Azure Fundamentals -- Practice Tests

模擬試験が3つと、それぞれの回答解説が付いてきます。
英語ですが、Chromeの翻訳機能を使えば十分読めるレベルになりました。
結果的にはこちらを購入して、試験慣れしておいてよかったです。
Learnの読み込みだけだとちょっと足りなかったと感じました。
セール時の最低価格(自分は¥1,200で購入)で購入できたので費用対効果は十分でした。

ひとまず合格できて安心しましたが、
やはりAzureに関する本当に基礎的な知識が中心なので、
AZ-900保持だけだと現場で「Azure出来ます!」とアピールするレベルには到達出来ないと感じました。
なので、次はAZ-203の合格に向けて勉強を開始しました。

www.microsoft.com

AZ-203の試験範囲のラーニングパスをMicrosoft Learnで開始しましたが、
実際にサービスのリソースを作成したり、コードを書いたり手を動かすレッスンが中心なので、
AZ-203の勉強を通じて「Azure出来ます!」レベルに到達出来ることを期待しています。