- 投稿日:2020-10-11T21:33:10+09:00
Azure Functionsでアクティビティ関数を直接デバッグする
Azure Functionsで大規模なコードを開発していると、デバッグがめんどくさい・・・
HTTPトリガーをコールしてから目的の関数にたどり着くまで待ち時間が長いことも多い。
なので、直接アクティビティ関数をデバッグできないか調査。
MSの以下のドキュメントをを参考にした。
https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-manually-run-non-http「HTTP によってトリガーされない関数に HTTP 要求を介してアクセスするには、フォルダー admin/functions から要求を送信する必要があります。」とあるので、デバッグ時もそうすればいいのでは?という単純な思考でデバッグしてみる。
1. デバッグする関数
ソースは以下。(クリックするとソースが表示されます)
public static class Function2 { [FunctionName("Function2")] public static async Task<List<string>> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { var outputs = new List<string>(); // Replace "hello" with the name of your Durable Activity Function. outputs.Add(await context.CallActivityAsync<string>("Function2_Hello", "Tokyo")); outputs.Add(await context.CallActivityAsync<string>("Function2_Hello", "Seattle")); outputs.Add(await context.CallActivityAsync<string>("Function2_Hello", "London")); // returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"] return outputs; } [FunctionName("Function2_Hello")] public static string SayHello([ActivityTrigger] IDurableOrchestrationContext inputs, ILogger log) //public static string SayHello([ActivityTrigger] string name, ILogger log) { var name = inputs.GetInput<string>(); log.LogInformation($"Saying hello to {name}."); return $"Hello {name}!"; } [FunctionName("Function3")] public static string SayHello3([ActivityTrigger] string inputString, ILogger log) { var input = JsonConvert.DeserializeObject<Function3Input>(inputString); var name = input.Name; log.LogInformation($"Saying hello to {name}."); return $"Hello {name}!"; } [FunctionName("Function2_HttpStart")] public static async Task<HttpResponseMessage> HttpStart( [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestMessage req, [DurableClient] IDurableOrchestrationClient starter, ILogger log) { // Function input comes from the request content. string instanceId = await starter.StartNewAsync("Function2", null); log.LogInformation($"Started orchestration with ID = '{instanceId}'."); return starter.CreateCheckStatusResponse(req, instanceId); } public class Function3Input { public string Name { get; set; } public string Age { get; set; } } }オーケストレーター関数を最初に作成したときのサンプルに毛をはやした(Functions3とFunction3Inputを足した)もの。
Functions2_HelloとFunctions3を直接呼び出せるか確認する。2. 結果
Functions2_Helloは呼び出せなかった。Function3は呼び出せた。
以下のようにリクエストするとFunction3が呼びだる。POST /admin/functions/Function3 HTTP/1.1 HOST: localhost:7071 content-type: application/json content-length: 56 { "input": "{\"name\": \"aaa\", \"age\": \"20\" }" }アクティビティ関数の引数にわたる文字列は「input」という固定のプロパティにJSON形式の文字列を渡せば、それがそのまま渡るみたい。
上記の場合、Function3の引数には「{"name":"aaa","age":"20"}」という文字列が渡される。ちなみに、Function2_Helloのほうを同じように呼び出すと、リクエストは成功(202 Accepted)するが、デバッグコンソール上でエラーが表示される。
POST /admin/functions/Function2_Hello HTTP/1.1 HOST: localhost:7071 content-type: application/json content-length: 54 { "input": "{\"inputs\" : {\"name\": \"aaa\"} }" }System.Private.CoreLib: Exception while executing function: Function2_Hello. Microsoft.Azure.WebJobs.Host: Exception binding parameter 'inputs'. Newtonsoft.Json: Could not create an instance of type Microsoft.Azure.WebJobs.Extensions.DurableTask.IDurableOrchestrationContext. Type is an interface or abstract class and cannot be instantiated. Path 'inputs', line 1, position 10.エラー見ると「インタフェースクラスなのでインスタンス化できません」って言われてるのでなんか無理そうだし調べるのめんどくさそうだしいいやってことで調べるのやめた()
もし呼び出せた方いたらやり方教えてください。
今日は以上~。
- 投稿日:2020-10-11T19:10:17+09:00
ASP.NET Core 3.1 MVC による Microsoft アカウント認証のロール ベース アクセス制御
はじめに
前回、以下の記事で、ASP.NET Core 3.1 MVC による Microsoft アカウント認証の実装手順を説明しました。
認証ができたら、次は、ロール ベース アクセス コントロール、つまり、コントローラーやアクションに、[Authorize(Roles="admin")] みたいな属性をつけて、特定のグループに所属している場合のみにアクセスを許可するといったことをやりたいのですが、Microsoft アカウント認証のみを対象としたときは、やり方が見つかりませんでした。
Azure Active Directory アカウントでは、以下の方法で、roles や groups をクレームに含めて、アカウントのロールとして管理、制御できます。
- How to: Add app roles in your application and receive them in the token
- Add authorization using groups & group claims to an ASP.NET Core Web app that signs-in users with the Microsoft identity platform
これらの方法の様に所属しているロールをクレームに含めることができれば、ロールの管理が容易となるのですが、今回は、アプリ側で、ロールを定義して ASP.NET Core のポリシーで認可を制御する方法を説明します。
ロールの定義
今回は、appSettings.json にロールと所属するアカウントを定義していきます。
ここでは、Admins を配列として定義して、その値に、所属するアカウントとして、対象アカウントのオブジェクト ID "http://schemas.microsoft.com/identity/claims/objectidentifier" の値を定義しています。appSettings.json{ "Admins": [ "00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000" ] }"http://schemas.microsoft.com/identity/claims/objectidentifier" の値は、実際に Microsoft アカウントを認証させて、確認しました。
以下の記事の手順で、Microsoft アカウント認証を行い、対象アカウントのオブジェクト ID を取得して、設定を行ってください。ASP.NET Core 3.1 MVC による Microsoft アカウント認証#動作確認
http://schemas.microsoft.com/identity/claims/objectidentifierポリシー ベースでの認可
Startup.ConfigureServices でポリシーを定義、追加します。
以下のコードでは、先ほど定義した appSettings.json の Admins 配列にオブジェクト ID が含まれているかを評価するポリシー "AdminOnly" を定義、追加しています。
AuthorizationPolicyBuilder.RequireAssertion を使うとクロージャーで簡単にポリシーを定義できます。Startup.cspublic void ConfigureServices(IServiceCollection services) { services.AddMicrosoftIdentityWebAppAuthentication(Configuration, "AzureAd"); services.AddControllersWithViews().AddMicrosoftIdentityUI(); services.Configure<AuthorizationOptions>(options => { options.AddPolicy("AdminOnly", policy => policy.RequireAssertion(context => { if (!context.User.Identity.IsAuthenticated) { return false; } // オブジェクト ID の取得 var oid = context.User.Claims.FirstOrDefault(claim => claim.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier"); // appSettings.json の Admins にオブジェクト ID が含まれているか評価 return this.Configuration.GetSection("Admins").Get<string[]>().Contains(oid?.Value); })); }); }コントローラーへのポリシーの設定
あとは、コントローラーやアクションに [Authorize] 属性とともに、ポリシーを指定します。
[Authorize(Policy = "ポリシー名")] でポリシー名を指定します。
AuthorizeAttribute のコンストラクターで、ポリシー名を指定できるので、[Authorize("ポリシー名")] とすることもできます。HomeController.cs[Authorize(Policy = "AdminOnly")] public IActionResult Edit() { return View(); } [Authorize("AdminOnly")] public IActionResult Delete() { return View(); }まとめ
ASP.NET Core 3.1 MVC による Microsoft アカウント認証 の手順で、Azure Active Directory にアプリを登録する際に、[サポートされているアカウントの種類] を "個人用 Microsoft アカウントのみ" とした時、つまり、Microsoft アカウント認証を選択した場合は、ロールの定義や定義したロールをクレームに含めることができないようです。
よって、今回は、ロールの定義をアプリ側で行い、ユーザー アカウントがロールに含まれていることを検証するポリシーを定義し、ポリシー ベースの認可で、ロール ベース ライクなアクセス制御を実現してみました。
実は、ASP.NET Core のミドルウェアで、クレームを追加して [Authorize(Roles="ロール名")] といったこともできるのですが、セキュリティ面でどうなのかという点と、ポリシー ベースの方が、色々と応用が利くと考えたので、今回は、ポリシー ベースでの説明としました。参考サイト
- 投稿日:2020-10-11T16:51:53+09:00
[.NET][C#]NUnitのCollectionAssertアサーションメソッド一覧
はじめに
前回、NUnitのStringAssertクラスのアサーションメソッドを一通り試したので
今回はCollectionAssertクラスのアサーションメソッドを一通り試してみる。実施環境
.NET:3.1.401
C#:8.0
NUnit:3.12.0CollectionAssertクラス
NUnitのClassicModelのアサーションクラスの1つ。
クラス名の通り、コレクション(IEnumerable実装クラス)を検査する。1. AllItemsAreInstancesOfType
コレクションが保持する要素の型が全て同じかどうかを検査する。
第1引数のコレクションが保持する要素が、第2引数に指定した型か、指定した型のサブクラスのみであればテストOKと判定される。C#のコレクションは、保持する型をジェネリクスで指定する
ICollection<T>
の実装クラスを使用するのが一般的なので
このアサーションを使用する機会はあまり無いだろう。public class Target {} public class TargetSub : Target {} public class CollectionAssertTest { [TestCase] public void AllItemsAreInstancesOfTypeTest() { // Test OK. CollectionAssert.AllItemsAreInstancesOfType( new ArrayList() {"hoge", "fuga", "hage"}, Type.GetType("System.String")); // Test NG. CollectionAssert.AllItemsAreInstancesOfType( new ArrayList() {"hoge", "fuga", new Target()}, Type.GetType("System.String")); // Test OK. CollectionAssert.AllItemsAreInstancesOfType( new ArrayList() {new Target(), new Target(), new TargetSub()}, Type.GetType("AssetionsTest.Target")); } }2. AllItemsAreNotNull
コレクションが保持する要素の中にnullが含まれるかどうかを検査する。
コレクションが空の場合はテストOKとなる。[TestCase] public void AllItemsAreNotNullTest() { // Test OK. CollectionAssert.AllItemsAreNotNull( new List<string>() {"hoge", "fuga", "hage"}); // Test NG. CollectionAssert.AllItemsAreNotNull( new List<string>() {"hoge", null, "hage"}); // Test OK. CollectionAssert.AllItemsAreNotNull( new List<string>() {}); }3. AllItemsAreUnique
コレクションが保持する要素が全てユニークなものかを検査する。
ユニーク性の判定はEqualsメソッド
で判定しているようで、
下記コードの1番下の例のように、参照が異なっていても値として同じものが含まれていればテストNGとなる。[TestCase] public void AllItemsAreUniqueTest() { // Test OK. CollectionAssert.AllItemsAreUnique( new List<string>() {"hoge", "fuga", "hage"}); // Test NG. CollectionAssert.AllItemsAreUnique( new List<string>() {"hoge", "hoge", "hage"}); // Test OK. CollectionAssert.AllItemsAreUnique( new List<string>() {"hoge", null, "hage"}); // Test NG. CollectionAssert.AllItemsAreUnique( new List<string>() {new String("hoge"), new String("hoge"), new String("hage")}); }4. AreEqual、AreNotEqual
AreEqualメソッドは、2つのコレクションが保持する要素が格納順序込みで同じものがどうかを検査する。
AreNotEqualメソッドは、その逆の判定を行う。下記コードの3つ目の例のように、保持する要素が同じでも、格納する順序が異なる場合、テストNGとなる。
順序を無視した検査をしたい場合は、AreEquivalentメソッド
を使う。[TestCase] public void AreEqualTest() { // Test OK. CollectionAssert.AreEqual( new List<string>() {"hoge", "fuga", "hage"}, new List<string>() {"hoge", "fuga", "hage"}); // Test NG. CollectionAssert.AreEqual( new List<string>() {"hoge", "fuga", "hage"}, new List<string>() {"hoge", "hoge", "hage"}); // Test NG. CollectionAssert.AreEqual( new List<string>() {"hoge", "fuga", "hage"}, new List<string>() {"fuga", "hoge", "hage"}); // Test OK. CollectionAssert.AreEqual( new List<string>() {"hoge", "fuga", "hage"}, new List<string>() {new String("hoge"), new String("fuga"), new String("hage")}); }5. AreEquivalent、AreNotEquivalent
AreEquivalentメソッドは、2つのコレクションが保持する要素が同じものがどうかを検査する。
AreNotEquivalentメソッドは、その逆の判定を行う。AreEqualメソッドとは違い、格納順は異なっていてもテストOKとなる。
[TestCase] public void AreEquivalentTest() { // Test OK. CollectionAssert.AreEquivalent( new List<string>() {"hoge", "fuga", "hage"}, new List<string>() {"hoge", "fuga", "hage"}); // Test NG. CollectionAssert.AreEquivalent( new List<string>() {"hoge", "fuga", "hage"}, new List<string>() {"hoge", "hoge", "hage"}); // Test OK. CollectionAssert.AreEquivalent( new List<string>() {"hoge", "fuga", "hage"}, new List<string>() {"fuga", "hoge", "hage"}); // Test OK. CollectionAssert.AreEquivalent( new List<string>() {"hoge", "fuga", "hage"}, new List<string>() {new String("hoge"), new String("fuga"), new String("hage")}); }6. Contains、DoesNotContain
Containsメソッドは、コレクションに指定した要素が含まれるかどうかを検査する。
DoesNotContainメソッドは、その逆の判定を行う。[TestCase] public void ContainsTest() { // Test OK. CollectionAssert.Contains( new List<string>() {"hoge", "fuga", "hage"}, "hoge"); // Test NG. CollectionAssert.Contains( new List<string>() {"hoge", "fuga", "hage"}, "HOGE"); // Test OK. CollectionAssert.Contains( new List<string>() {"hoge", "fuga", "hage"}, new String("hoge")); }7. IsSubsetOf、IsNotSubsetOf
IsSubsetOfメソッドは、コレクションが部分的に一致するかどうかを検査する。
IsNotSubsetOfメソッドは、その逆の判定を行う。第1引数に部分一致させるコレクションを指定、
第2引数に全体となるコレクションを指定する。[TestCase] public void IsSubsetOfTest() { // Test OK. CollectionAssert.IsSubsetOf( new List<string>() {"hoge", "fuga"}, new List<string>() {"hoge", "fuga", "hage"}); // Test NG. CollectionAssert.IsSubsetOf( new List<string>() {"HOGE", "FUGA"}, new List<string>() {"hoge", "fuga", "hage"}); // Test NG. CollectionAssert.IsSubsetOf( new List<string>() {"hoge", "fuga", "hage"}, new List<string>() {"hoge", "fuga"}); }8. IsEmpty、IsNotEmpty
IsEmptyメソッドは、コレクションが空であることを検査する。
IsNotEmptyメソッドは、その逆の判定を行う。nullが入っていてもコレクション自体は空ではないのでテスト結果NGとなる。
[TestCase] public void IsEmptyTest() { // Test OK. CollectionAssert.IsEmpty( new List<string>() {}); // Test NG. CollectionAssert.IsEmpty( new List<string>() {"hoge"}); // Test NG. CollectionAssert.IsEmpty( new List<string>() {null}); }9. IsOrdered
コレクションの順序を検査する。
引数にコレクションオブジェクトのみ指定した場合、自然順で要素が格納されていることを検査する。
検査する順序を指定したい場合は、下記コードの一番下の例のように、第2引数にIComparerインターフェース
を実装したクラスのインスタンスを渡す。[TestCase] public void IsOrderedTest() { // Test OK. CollectionAssert.IsOrdered( new List<string>() {"A", "B", "C"}); // Test NG. CollectionAssert.IsOrdered( new List<string>() {"C", "B", "A"}); // Test OK. CollectionAssert.IsOrdered( new List<string>() {null, "A", "B"}); // Test NG. CollectionAssert.IsOrdered( new List<string>() {"A", "B", null}); // Test ArgumentException. CollectionAssert.IsOrdered( new ArrayList() {"A", "B", new Target()}); // Test OK. CollectionAssert.IsOrdered( new List<string>() {"C", "B", "A"}, new StringOrderDesc() ); } public class StringOrderDesc : Comparer<string> { public override int Compare(string x, string y) => y.CompareTo(x); }nullは自然順序的には最も小さい値という扱いらしい。
また、型が混在している場合、
ArgumentException
が発生する。
(ジェネリクスを使用していれば発生しないだろうが)ちなみに
IComparer<T>インターフェース
はIComparerインターフェース
を継承していないので(なぜだろうか・・・)
IComparer<T>インターフェース
を実装したクラスを第2引数に渡すことができない。
Comparer<string>クラス
はその両方をimplementsしているので、ジェネリクスを使用して無用なキャストを避けたい場合は、Comparerクラスのサブクラスを用意する。参考
- 投稿日:2020-10-11T02:41:50+09:00
Unity CommandBufferを用いて選択したオブジェクトにだけアウトラインをつける
概要
以下のgifのように、クリックしたオブジェクトにのみアウトラインを設定するようなものを、Unityの機能であるCommandBufferを利用して作成しました。
プロジェクトのソースコードはこちら
https://github.com/Arihide/unity-selective-outline解説
CommandBufferを用いると、レンダリングパイプラインの任意の箇所に、別の描画処理を挟み込むことができます。
今回はこの機能を用いて、以下の図のように、一部メッシュに対してだけレンダリング・輪郭抽出を行った後に合成を行いました。
次からは具体的なスクリプト部分について説明します。
スクリプト部分
まずは、CommandBufferを用いたスクリプト部分を見てみましょう。
SelectiveOutline.csusing UnityEngine; using UnityEngine.Rendering; [ExecuteInEditMode] [RequireComponent(typeof(Camera))] public class SelectiveOutline : MonoBehaviour { public Material emissionMaterial; public Material outlineMaterial; private new Camera camera; private CommandBuffer commandBuffer; [SerializeField] private Renderer targetRenderer = null; void OnEnable() { camera = GetComponent<Camera>(); commandBuffer = new CommandBuffer(); commandBuffer.name = "Selective Outline"; SetCommandBuffer(); // ImageEffects前(OnRenderImageが呼ばれる前)に適用 camera.AddCommandBuffer(CameraEvent.BeforeImageEffects, commandBuffer); } void OnDisable() { camera.RemoveCommandBuffer(CameraEvent.BeforeImageEffects, commandBuffer); } void SetCommandBuffer() { commandBuffer.Clear(); if (targetRenderer != null) { // レンダリング結果を格納するテクスチャ作成 var id = Shader.PropertyToID("_OutlineTex"); commandBuffer.GetTemporaryRT(id, -1, -1, 24, FilterMode.Bilinear); commandBuffer.SetRenderTarget(id); // アウトラインを表示させたいメッシュの描画 commandBuffer.ClearRenderTarget(false, true, Color.clear); commandBuffer.DrawRenderer(targetRenderer, emissionMaterial); // アウトラインを抽出して合成 commandBuffer.Blit(id, BuiltinRenderTextureType.CameraTarget, outlineMaterial); } } void Update() { if (Input.GetMouseButtonDown(0)) { Ray ray = camera.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out RaycastHit hit)) { targetRenderer = hit.transform.GetComponent<Renderer>(); SetCommandBuffer(); } } } }具体的な内容はソースコードのコメントを見ていただくとして、
大まかな処理の流れとしては、
OnEnable
関数内でCommandBufferオブジェクトを作成し、さらにSetCommandBuffer
関数内でどのような流れで描画するのかを設定しています。
その後、camera.AddCommandBuffer(CameraEvent.BeforeImageEffects, commandBuffer);とすることによって、設定したCommandBufferをImageEffectsの直前(一通りメッシュの描画が完了したあと)に挟み込むようにします。
また
Update
関数内でオブジェクトがクリックされたのを検出したとき、そのオブジェクトにアウトラインをつけるように、CommandBufferの再設定を行っています。シェーダー部分
さて、処理の流れがわかったところで、次にアウトラインを抽出するシェーダーを見てみましょう。
Outline.shaderShader "Custom/Outline" { Properties { [HideInInspector]_MainTex ("Texture", 2D) = "white" {} _OutlineColor ("Outline Color", Color) = (1,1,1,1) _OutlineWidth ("Outline Width", Range(0, 10)) = 1 } SubShader { Tags { "Queue" = "Transparent" } Blend SrcAlpha OneMinusSrcAlpha Cull Off ZWrite Off ZTest Always Pass { CGPROGRAM #pragma vertex vert_img #pragma fragment frag #include "UnityCG.cginc" sampler2D _MainTex; half2 _MainTex_TexelSize; half4 _OutlineColor; half _OutlineWidth; half4 frag (v2f_img i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); half2 destUV = _MainTex_TexelSize * _OutlineWidth; half left = tex2D(_MainTex, i.uv + half2(destUV.x, 0)).a; half right = tex2D(_MainTex, i.uv + half2(-destUV.x, 0)).a; half bottom = tex2D(_MainTex, i.uv + half2(0, destUV.y)).a; half top = tex2D(_MainTex, i.uv + half2(0, - destUV.y)).a; half topLeft = tex2D(_MainTex, i.uv + half2(destUV.x, destUV.y)).a; half topRight = tex2D(_MainTex, i.uv + half2(-destUV.x, destUV.y)).a; half bottomLeft = tex2D(_MainTex, i.uv + half2(destUV.x, -destUV.y)).a; half bottomRight = tex2D(_MainTex, i.uv + half2(-destUV.x, -destUV.y)).a; // あるピクセルの近傍が不透明であれば 1 half result = saturate(left + right + bottom + top + topLeft + topRight + bottomLeft + bottomRight); // 透過じゃないところはそのまま clip(0.99 - col.a); half4 outline = result * _OutlineColor; return outline; } ENDCG } } }よく見かけるアウトラインシェーダーは深度や法線による方法が多いですが、
今回はあるピクセルの近くが不透明だったら自分のピクセルも不透明とみなして輪郭を広げ、もともと不透明だったピクセル部分はくり抜く。という単純な方法で実装しました。
理由としては今回は描画対象が1つなので、重なりなどを考慮する必要がないからです。
- 投稿日:2020-10-11T01:45:58+09:00
タイマー作成①
初めに
新卒でIT系の企業に就職して半年がたち、10月から本格的に業務に参加させていただいています。もやしと申します。
現在配属している部署で、月に1度、自身が行っている勉強や、それによる成長度合い等の発表を
行わなければならなくなったさせていただく事になりました。せっかくなので、勉強の過程で得られた知識やプログラムを共有したく、今回Qiitaに登録しました!
読みにくい文章ですが、興味のある方は、ぜひ読んでいってください。本題
会社の新卒研修で作成したタイマーソフトを、もう一度作り直そうと思います。
というのもこの作成したタイマーソフト、今見直すと酷過ぎるプログラムでして・・・。黒歴史になりかねないので、今一度誰もが納得する出来にしたいです!要求仕様書
うろ覚えですが、以下に与えられた要求仕様書を載せておきます。
研修では、細かい仕様をサポーターの方と話し合いながら決めましたが、今回は自分で決めようと思います。
- 20分50秒までカウントするタイマーを作成してください。
- 分数を設定するボタンを1回押すと1分カウントアップし、秒数を設定するボタンを1回押すと10秒カウントアップするようにしてください。
- クリアキーを押すと、設定した時間がリセットされるようにしてください。
- タイマースタート・ストップは1つのキーで制御してください。
- 時間の設定は、カウントダウンが停止している時のみ行えるようにしてください。
- タイマーが0になったら、「タイマーが終了しました」と3秒間表示し、3秒たったら自動的に時間設定を行えるようにしてください。
- ただし、「タイマーが終了しました」と表示中に何らかのキーが押された場合、強制的に時間設定が行えるようにしてください。
長くなりましたが、以上が仕様書となります。図にすると、こんな感じですかね。
環境
IDE:Visual Studio Express 2017
言語:C#
フォームアプリケーションではなく、コンソールアプリケーションとして作成します。
コーディング規約
実際に業務を始めてから痛感していることですが、コーディング規約を守るのは非常に大切ですね。
今回はマイクロソフトドキュメントのコーディング規約に従ってコーディングしていきます。メソッド名を作成者の名前にするのは犯罪です。絶対にやめましょう。(個人単位のデバッグ用なら良い)
前置きは以上となります。
次回からは実際にコーディングを行っていくので、よろしければ次回も読んでくださいね。
- 投稿日:2020-10-11T00:02:10+09:00
[.NET][C#]NUnitのStringAssertアサーションメソッド一覧
はじめに
前回、NUnitのAssertクラスのClassicModelのアサーションメソッドを一通り試したので
今回はStringAssertクラスのアサーションメソッドを一通り試してみる。実施環境
.NET:3.1.401
C#:8.0
NUnit:3.12.0StringAssertクラス
NUnitのClassicModelのアサーションクラスの1つ。
クラス名の通り、文字列を検査する。
全てのメソッドは大文字小文字を区別して判定する。1. Contains、DoesNotContain
Containsメソッドは、第1引数の文字列が第2引数の文字列内に含まれていることを検査する。
DoesNotContainメソッドは、その逆で第1引数の文字列が第2引数の文字列内に含まれていないことを検査する。[TestCase("hoge", "aaahogeaaa")]// Test OK. [TestCase("hoge", "aaahogaaa")]// Test NG. [TestCase("hoge", "aaaHOGEaaa")]// Test NG. public void ContainsTest(string expected, string actual) { StringAssert.Contains(expected, actual); } [TestCase("hoge", "aaahogeaaa")]// Test NG. [TestCase("hoge", "aaahogaaa")]// Test OK. [TestCase("hoge", "aaaHOGEaaa")]// Test OK. public void DoesNotContainTest(string expected, string actual) { StringAssert.DoesNotContain(expected, actual); }2. StartsWith、DoesNotStartWith
StartsWithメソッドは、第1引数の文字列が第2引数の文字列の接頭辞になっていることを検査する。
DoesNotStartWithメソッドは、第1引数の文字列が第2引数の文字列の接頭辞になっていないことを検査する。[TestCase("hoge", "hogeaaa")]// Test OK. [TestCase("hoge", "hogaaa")]// Test NG. [TestCase("hoge", "HOGEaaa")]// Test NG. public void StartsWithTest(string expected, string actual) { StringAssert.StartsWith(expected, actual); } [TestCase("hoge", "hogeaaa")]// Test NG. [TestCase("hoge", "hogaaa")]// Test OK. [TestCase("hoge", "HOGEaaa")]// Test OK. public void DoesNotStartWithTest(string expected, string actual) { StringAssert.DoesNotStartWith(expected, actual); }ちなみに、公式サイトのドキュメントでは、
DoesNotStartsWithと記載されているが、
DoesNotStartWithが正しい。
(StartsWithは、Withの前にsがあるので実装ミス?)3. EndsWith、DoesNotEndWith
StartsWithの接尾辞版。
[TestCase("hoge", "aaahoge")]// Test OK. [TestCase("hoge", "aaahog")]// Test NG. [TestCase("hoge", "aaaHOGE")]// Test NG. public void EndsWithTest(string expected, string actual) { StringAssert.EndsWith(expected, actual); } [TestCase("hoge", "aaahoge")]// Test NG. [TestCase("hoge", "aaahog")]// Test OK. [TestCase("hoge", "aaaHOGE")]// Test OK. public void DoesNotEndWithTest(string expected, string actual) { StringAssert.DoesNotEndWith(expected, actual); }4. AreEqualIgnoringCase、AreNotEqualIgnoringCase
AreEqualIgnoringCaseメソッドは、第1引数と第2引数の文字列が大文字小文字無視で一致していることを検査する。
AreNotEqualIgnoringCaseメソッドは、第1引数と第2引数の文字列が大文字小文字無視で一致していないことを検査する。
当然だが、半角全角を区別しないというわけではない。[TestCase("hoge", "HOGE")]// Test OK. [TestCase("hoge", "Hoge")]// Test OK. [TestCase("hoge", "HOG")]// Test NG. [TestCase("hoge", "hoge")]// Test NG. public void AreEqualIgnoringCaseTest(string expected, string actual) { StringAssert.AreEqualIgnoringCase(expected, actual); } [TestCase("hoge", "HOGE")]// Test NG. [TestCase("hoge", "Hoge")]// Test NG. [TestCase("hoge", "HOG")]// Test OK. [TestCase("hoge", "hoge")]// Test OK. public void AreNotEqualIgnoringCaseTest(string expected, string actual) { StringAssert.AreNotEqualIgnoringCase(expected, actual); }5. IsMatch、DoesNotMatch
IsMatchメソッドは、第1引数の正規表現パターンが第2引数の文字列にマッチするか検査する。
DoesNotMatchメソッドは、第1引数の正規表現パターンが第2引数の文字列にマッチしないか検査する。[TestCase("^[0-9]{3}hoge[A-Z]{3}$", "123hogeABC")]// Test OK. [TestCase("[0-9]*", "hoge123hoge")]// Test OK. [TestCase("^[0-9]{3}hoge[A-Z]{3}$", "ABChoge123")]// Test NG. public void IsMatchTest(string regexPattern, string actual) { StringAssert.IsMatch(regexPattern, actual); } [TestCase("^[0-9]{3}hoge[A-Z]{3}$", "123hogeABC")]// Test NG. [TestCase("[0-9]*", "hoge123hoge")]// Test NG. [TestCase("^[0-9]{3}hoge[A-Z]{3}$", "ABChoge123")]// Test OK. public void DoesNotMatchTest(string regexPattern, string actual) { StringAssert.DoesNotMatch(regexPattern, actual); }参考