【C# / ASP.NET WebForm】HttpRequestBase を継承しMock クラスを作成し、HttpRequest をテストする
前置き
現場の案件で、外部APIやDB接続が絡んだ少々複雑な判定でCookie を作成するという処理を実装することになり、
対象の処理でユニットテストを作成しておきたくなりました。
Cookie の作成や、作成した値を検証するには当然ですがHttpRequest
やHttpResponse
のインスタンスが必要ですが、
これらのクラスのインスタンスをユニットテストで生成するのは困難(ほぼ不可能のはず)です。
そこで、ASP.NET ではHttpRequestWrapper
というクラスが実装されています。
HttpRequestWrapper
はHttpRequestBase
という抽象クラスから派生されています。
HttpRequestBase
はHttpRequest
と同じプロパティ、関数を保有したクラスです。
対象となる処理の実装ではHttpRequestBase
を使用し、
HttpRequestWrapper
でHttpRequest
をラップしたインスタンスを使用することにします。
これらは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にアップしました。
【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の以下の演習内で引っかかった箇所があったのでメモ。
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】「区分オブジェクト」のようなクラスを作れないか試行錯誤
前回の「区分オブジェクト」に関する投稿に引き続いて関連した投稿を。
列挙型の区分ごとにプロパティを持たせる事ができないかなと思い、
実装してみたのが以下のような抽象クラスと具象クラスを使う形式。
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
実行すると以下のようになります。
うーん・・・具象クラスのコンストラクタを全部書く必要があるのと、
Activator.CreateInstance
でType名をセットする部分があまりイケてない感じがする・・・
参考
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」に配置した。
参考
VB.NETでJavaの「列挙型クラス」を模倣して「区分オブジェクト」を作る
増田本、読了しました。
Amazonのレビュー見ると結構賛否両論のようですが、
オブジェクト指向設計、ドメイン駆動設計の基礎固めとして最適では、と自分は感じました。
現場で役立つシステム設計の原則 ~変更を楽で安全にするオブジェクト指向の実践技法
- 作者: 増田亨
- 出版社/メーカー: 技術評論社
- 発売日: 2017/07/05
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
「CHAPTER2 場合分けのロジックを整理する > Javaの列挙型を使えばもっとかんたん」を読んで、
おお、JavaはEnumの値ごとにコンストラクタを付与したりできるのか・・・便利・・・
と恥ずかしながら初めて知りました。
.NETでも似たようなことをできないなかな、と探したら以下のようなMS公式リファレンスがありました。
上記と増田本のサンプルコードを真似して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
実行してみましょう。
値取れてますね。
良い感じですが、リフレクションで変数名を指定するのはちょっと怖いです。以上。
【読了】「Adaptive Code ~ C#実践開発手法 第2版」
Adaptive Code ~ C#実践開発手法 第2版 (マイクロソフト関連書)
- 作者: Gary McLean Hall,長沢智治(監訳),クイープ
- 出版社/メーカー: 日経BP
- 発売日: 2018/02/24
- メディア: 単行本
- この商品を含むブログ (1件) を見る
「Adaptive Code ~ C#実践開発手法 第2版」を読了しました。
学び
第3章 依存関係と階層化
3.3.2 横断的関心事
横断的関心事(ロギングやトランザクション管理など)は、
カプセル化した上でコードに適用するのが悪影響の少ない方法。
アスペクト指向プログラミングの導入→C#のAORライブラリとして「PostSharpがおすすめ。」
第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部以降ではコード例なども交えて実践的な開発手法についての解説に入っていく。
- デザインパターン(Null Object / Adapter / Strategy / Builder / Composite / etc... )
- ユニットテスト、特に保守可能なテストコード
- テスト駆動開発
- リファクタリング
- SOLIDの5つの原則
- 単一債務の原則( Single Resoponsibility Principle)
- 開放/閉鎖の原則( Open / Closed Principle )
- リスコフの置換原則( Liskov's Substitution Principle )
- インターフェイス分離の原則( Interface Segregation Principle )
- 依存性反転の原則( Dependency Inversion Principle )
- 依存性注入
- 結合性と凝集性
- ドメイン駆動設計 / AOR
などなど多岐にわたるプラクティス・原則について、実践的なコード例を多数交えて紹介している。
コード例も、参考書等でよくある説明のために簡素化された形ではなく、タイトルどおり「実践的」であると言える。 前述した「テストのBuilderパターン」などは早速現場で利用し始めている。
かなりのボリュームではあるが、オブジェクト指向の初心者/中級者が上級者にステップアップしていくために
必ず役に立つ1冊であると断言できる。
※Amazonのレビューが2件しかないのが本当に不思議。
【Azure】AZ-900:Microsoft Azure Fundamentals に合格しました
今月頭にAZ-900:Microsoft Azure Fundamentalsを受験し合格しました。
勉強方法としては、まずMicrosoft Learnの以下のラーニングパスを読み込みました。
さらに自分は心配だったため、以下のUdemyのコースを購入しました。
AZ-900 -- Microsoft Azure Fundamentals -- Practice Tests
模擬試験が3つと、それぞれの回答解説が付いてきます。
英語ですが、Chromeの翻訳機能を使えば十分読めるレベルになりました。
結果的にはこちらを購入して、試験慣れしておいてよかったです。
Learnの読み込みだけだとちょっと足りなかったと感じました。
セール時の最低価格(自分は¥1,200で購入)で購入できたので費用対効果は十分でした。
ひとまず合格できて安心しましたが、
やはりAzureに関する本当に基礎的な知識が中心なので、
AZ-900保持だけだと現場で「Azure出来ます!」とアピールするレベルには到達出来ないと感じました。
なので、次はAZ-203の合格に向けて勉強を開始しました。
AZ-203の試験範囲のラーニングパスをMicrosoft Learnで開始しましたが、
実際にサービスのリソースを作成したり、コードを書いたり手を動かすレッスンが中心なので、
AZ-203の勉強を通じて「Azure出来ます!」レベルに到達出来ることを期待しています。