20211015のC#に関する記事は3件です。

【Unity】Unityのダッシュボードのレイアウト変わってたから実装手順は俺が書く

Unityダッシュボードのレイアウト変わっててUnityAdsの導入手順が分からない 掲題の通りだ。金が欲しいのでUnityでゲーム作って広告入れようと思ったら、ダッシュボードのレイアウトが大きく変わったため既存の導入手順系の記事通りに行かなくなってしまった。 「いや公式リファレンス読めばわかるじゃんwww」という方は帰ってどうぞ。君らはターゲットじゃない。 同じことで悩んでいる初学者のみんな、頑張りましょう。公式リファレンスは俺が代わりに読んどくよ。 内容 一応、公式リファレンスがどこから見られるか書いとくよ   以下を参考にしてね※Unity用のページの開き方だよ これでIntegration guide for Unityってページが開くよ。 ダッシュボード画面のガイドはこっち あとはこのリファレンス通りに作業するだけだから、英語強い人は上のサイト見てね。 準備するよ ビルドターゲットを変更 ここではAndroidにするよ。UnityEditor左上の[File]>[Build Settings]を押すよ 右下に[Switch Platform]ってボタンが出るはずなので、それを押すよ UnityAdsをインストールするよ UnityEditor左上の[Window]>[PackageManager]を押してね AdvertisementをImportするよ こんな感じの画面が出るから、Advertisementを探してね。右下(この画像だと[Up to date]ボタンの位置)に[Install]ってボタンがあるから押してね。なんか[Import]みたいな画面でるはずだから、色々先に進みそうなボタン押してね。 ダッシュボードで広告の設定するよ ここ押すよ 最初どんな画面が出るか忘れちゃった。何かしらやって、以下の画面になったら[Add Ad Unit]を押してね こんな画面になるから、適宜情報を入力しよう。 - [Rewarded]はスキップできない広告で、見終わるとプレイヤーにゲーム内通貨とかの報酬を与えられるやつ。漫画アプリとかで広告みたら一話読めたりするよね。 - [Interstitial]は一定秒数経過で広告をスキップできる。基本的にはこっち選んどこうね。面倒くさいからね。 こんなんでるよ [+Advanced Settings]で表示する広告について音出すかとか、何秒で飛ばせるかとか決められるみたい。決定ボタンみたいなのはないから、設定終わったら[←]を押してひとつ前の画面に戻る そしたら一番下にこんな感じで今作った広告の設定?が表示されるはず もっと詳しく設定したい人はこっち 組み込んでいくよ じゃあそろそろゲームの方に組み込んでいこう。 まずGameIDを調べよう。ダッシュボード上で、さっき開いてたAd Unitsのページをもう一回開いてね。 ここにApple用のGameIDとAndroid用のGameID載ってるから、控えといてね。 したらScene内に空のGameObject置くか、ゲーム開始時点~ゲーム中も存在するObjectに以下の内容のScriptをアタッチしてあげてね。 ※リファレンスからの丸ごとコピーだから、もし権利者に怒られたら消すよ using UnityEngine; using UnityEngine.Advertisements; public class AdsInitializer : MonoBehaviour, IUnityAdsInitializationListener { [SerializeField] string _androidGameId; [SerializeField] string _iOsGameId; [SerializeField] bool _testMode = true; [SerializeField] bool _enablePerPlacementMode = true; private string _gameId; void Awake() { InitializeAds(); } public void InitializeAds() { _gameId = (Application.platform == RuntimePlatform.IPhonePlayer) ? _iOsGameId : _androidGameId; Advertisement.Initialize(_gameId, _testMode, _enablePerPlacementMode, this); } public void OnInitializationComplete() { Debug.Log("Unity Ads initialization complete."); } public void OnInitializationFailed(UnityAdsInitializationError error, string message) { Debug.Log($"Unity Ads Initialization Failed: {error.ToString()} - {message}"); } } InspectorからAndroid Game Id, iOsGameIdのとこに、さっき控えたandroidのGameIDとiOSのGameIDを入力しよう ここまでで広告表示のためのSDK初期化ができたよ。あと少し! さっきのスクリプトと同じGameObjectに以下のScriptをアタッチしてあげてね。 using UnityEngine; using UnityEngine.Advertisements; public class InterstitialAdExample : MonoBehaviour, IUnityAdsLoadListener, IUnityAdsShowListener { [SerializeField] string _androidAdUnitId = "Interstitial_Android"; [SerializeField] string _iOsAdUnitId = "Interstitial_iOS"; string _adUnitId; void Awake() { // Get the Ad Unit ID for the current platform: _adUnitId = (Application.platform == RuntimePlatform.IPhonePlayer) ? _iOsAdUnitId : _androidAdUnitId; } // Load content to the Ad Unit: public void LoadAd() { // IMPORTANT! Only load content AFTER initialization (in this example, initialization is handled in a different script). Debug.Log("Loading Ad: " + _adUnitId); Advertisement.Load(_adUnitId, this); } // Show the loaded content in the Ad Unit: public void ShowAd() { // Note that if the ad content wasn't previously loaded, this method will fail Debug.Log("Showing Ad: " + _adUnitId); Advertisement.Show(_adUnitId, this); } // Implement Load Listener and Show Listener interface methods: public void OnUnityAdsAdLoaded(string adUnitId) { // Optionally execute code if the Ad Unit successfully loads content. } public void OnUnityAdsFailedToLoad(string adUnitId, UnityAdsLoadError error, string message) { Debug.Log($"Error loading Ad Unit: {adUnitId} - {error.ToString()} - {message}"); // Optionally execite code if the Ad Unit fails to load, such as attempting to try again. } public void OnUnityAdsShowFailure(string adUnitId, UnityAdsShowError error, string message) { Debug.Log($"Error showing Ad Unit {adUnitId}: {error.ToString()} - {message}"); // Optionally execite code if the Ad Unit fails to show, such as loading another ad. } public void OnUnityAdsShowStart(string adUnitId) { } public void OnUnityAdsShowClick(string adUnitId) { } public void OnUnityAdsShowComplete(string adUnitId, UnityAdsShowCompletionState showCompletionState) { } public void OnGUI() { if (GUILayout.Button("LoadAndShowAd")) { LoadAd(); ShowAd(); } } } これでゲームを再生してみてね。画面上方に以下みたいな感じで[LoadAndShowAd]ってボタンが出てくるから、これを押してみてね。 広告が表示されると思う。されなかった?困っちゃうね。(投げやり) たぶんね、Inspector上でInterstitialAdExampleのAndroid Ad Unit Idを、さっき作った広告設定のIDに書き換えるとそれを表示するようにできるんですよ。 でもね、上述の手順だけだとその作成した広告設定の情報が穴だらけで使えないみたい。だからそこは自分で調べてみてね。 僕も調べるから、また進捗あれば更新するよ。 以上 想像以上に雑な内容でビビったかな?ごめんね、でも英語分からなくても広告が出せるとこまではたぶんいったよね?それで許してね。 今後も僕自身が「これ調べても出てこねーじゃんかよォ!!!!」て激怒することがあれば、こんな感じでHowTo記事にしようと思うよ。じゃあの。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jenkinsのvs-code-metricsプラグインが参照しているライブラリがcoreからなくなったとかなんとかで動かなくなっていた

ので、なんとかしてみた。 coreからなくなっている原因ははこのあたりを参照。 https://github.com/jenkinsci/jenkins/pull/5320 で、↑の対応の影響がvs-code-metricsプラグインにも出ているわけですが それに対応してくれている人がいてpull requestを出している https://github.com/jenkinsci/vs-code-metrics-plugin/pull/5 …のだけど本流にマージされていない。(2021-10-15時点) ので、マージされてないけどソースを落としてきて自分でビルドして vs-code-metrics.hpi を作ってみた。 そもそも、今後メンテナンスがあまりされないであろうプラグインっぽいのでご利用は計画的に…
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# の単体テストでstaticメソッドをモック化する方法

拡張メソッドをモック化できない! C# の単体テストでよく使われるモックライブラリといえば Moq や NMock でしょうか(私は Moq を使っています)。 これらのモックライブラリは static メンバをモック化することができないので、拡張メソッドを呼び出すようなテスト対象があると困ります。拡張メソッドの対象は自分で手を入れられないものも多いので(なので拡張メソッドで拡張しているわけで)。。。 そんなときの解決方法を幾つか紹介します。 課題 例えば以下のような状況です。 ILibraryClassインターフェースとその実装クラスLibraryClassが、参照している外部ライブラリで公開されている、という想定です。 // 拡張メソッド public static class LibraryClassExtensions { // このメソッドをモック化できない public static IEnumerable<object> GetElements(this ILibraryClass obj) { // ... } } // テスト対象クラスの利用例 public class SampleClass { public void GetElementCount(ILibraryClass obj) { // 拡張メソッドを使っている var specifiedElements = obj.GetElements(...); return specifiedElements.Count; } } // テストコード例 [TestClass] public class SampleClassTests { [TestMethod] public void FuncTest() { var obj = new LibraryClass(); var sample = new SampleClass(); // ★拡張メソッドGetElements()をモック化できないので、GetElementCount()をテストできない var count = sample.GetElementCount(obj); Assert.AreEqual(3, count); } } ライブラリを使う方法 Fakesを使う MicrosoftのFakes(古くはMoles)を使用すれば、Shimを使ってstaticクラスをモック化することができます。 Fakes : https://docs.microsoft.com/ja-jp/visualstudio/test/using-shims-to-isolate-your-application-from-other-assemblies-for-unit-testing?view=vs-2019 ただし Visual Studio の Enterprise エディションが必要です。 ライセンスがあるなら一番よい方法と思いますが、Professionalエディションでは使用できないというのはかなり高めのハードルです。 Professionalエディションでも使えるようにとコミュニティでも要望が登録されていますが、少なくともVS2019の間は対応しないそうです。 // テストコード例 [TestClass] public class SampleClassTests { [TestMethod] public void FuncTest() { var obj = new LibraryClass(); var sample = new SampleClass(); // FakesのShimを使えばstaticメソッドもモック化できる using (ShimsContext.Create()) { var expectModels = new Model[]{ new Model(), new Model(), new Model() }; ShimLibraryClassExtensions.GetElementsILibraryClass = obj => expectModels; var count = sample.GetElementCount(obj); Assert.AreEqual(3, count); } } } Poseを使う Pose は .NET Standard をターゲットとした OSS(MITライセンス)ライブラリです。 staticや非virtualでもデリデートに置き換えることができます。すごい! Pose : https://github.com/tonerdo/pose テスト対象コードには一切手を加えない(実行時のバイナリ書き換えもしない)ためテストの信頼性も問題ありません。 後述のような独自に工夫するよりよほどコスト対効果高いと思います。 ダウンロード数は結構ありますが、ライブラリの開発は止まっているようです。使用される場合はご注意ください。 using Pose; // テストコード例 [TestClass] public class SampleClassTests { [TestMethod] public void FuncTest() { var obj = new LibraryClass(); var sample = new SampleClass(); // Poseでモック化 var expectModels = new Model[]{ new Model(), new Model(), new Model() }; Shim s = Shim.Replace(() => ILibraryClassExtensions.GetElements(Is.A<LibraryClass>())) .With(obj => expectModels); // コンテキスト内で実行するとモックが使える PoseContext.Isolate(() => { var count = sample.GetElementCount(obj); Assert.AreEqual(3, count); }, s); } } ライブラリを使わない方法 OSSの利用が制限されている場合は独自に工夫する必要があります。 これまでに私が使ったこと(作ったこと)がある解決手法を3つ紹介します。 手法 概要 拡張メソッド テスト対象 テストコード 手法1 テスト対象クラスのモック化 テスト対象クラスから直接拡張メソッドを呼び出さず、拡張メソッドを呼び出す仮想メソッドを用意して、これをモック化する。 ⭕ ? ? 手法2 モック化可能な拡張メソッド呼び出しクラス定義 拡張メソッドを呼び出すモック化可能なクラスを定義し、これをモック化する。 ⭕ ❌ ⭕ 手法3 モック用インターフェース定義 単体テスト用に拡張メソッドの対象を派生したインターフェースまたは抽象クラスを定義し、拡張メソッド内で判断、分岐する。 ? ⭕ ⭕ どの手法もどこかに追加、変更が必要になり、違和感を感じるものもあります。 どれが優れているというわけではなく、各自のスタイルやポリシーに基づいて判断することになるでしょう。 個人的にはテストの都合で テスト対象を変更しなくてもよい手法3 が好きです(拡張メソッドは変更必要ですが1行だけですし)。 手法1 対象クラスのモック化 テスト対象クラスから直接拡張メソッドを呼び出さず、拡張メソッドを呼び出す仮想メソッドを用意して、これをモック化します。 評価項目 評価 理由 拡張メソッド ⭕ 変更不要。 テスト対象 ? 拡張メソッドを呼び出す仮想メソッドが必要だが、テスト専用というわけではない。 テストコード ? テスト対象クラスをモック化する点が違和感。 3つの手法の中で最も変更規模が小さく、コードも直感的に把握しやすいと思います。 // テスト対象クラスの利用例 public class SampleClass { public void GetElementCount(ILibraryClass obj) { var specifiedElements = CallGetElements(); return specifiedElements.Count; } // モック化したいメソッドを呼び出す仮想メソッドを定義 protected virtual IEnumerable<object> CallGetElements() { // 拡張メソッドを呼び出し return this.GetElements(); } } // テストコード例 using Moq; [TestClass] public class SampleClassTests { [TestMethod] public void FuncTest() { var sampleMock = new Mock<SampleClass>(); // ★CallGetElements()をモック化 var expectModels = new Model[]{ new Model(), new Model(), new Model() }; sampleMock.Setup(m => m.CallGetElements).Returns(expectModels); var obj = new LibraryClass(); var count = sampleMock.Object.GetElementCount(obj); Assert.AreEqual(3, count); } } 手法2 モック化可能な拡張メソッド呼び出しクラス定義 拡張メソッドを呼び出すモック化可能なクラスを定義して、これをモック化します。 テスト対象クラスそのものはあまり汚しませんが、専用クラスの導入が必要になります。 評価項目 評価 理由 拡張メソッド ⭕ 変更不要。 テスト対象 ❌ 拡張メソッドを呼び出すためのクラスを渡す必要がある。テスト専用である。 テストコード ⭕ 違和感なし。 手法1 と違うのは、このクラスをDI登録してテスト対象クラスにコンストラクタ注入することで、拡張メソッド群(が拡張する外部ライブラリ)とテスト対象クラスの疎結合化を進める第一歩と考えることもできます。 // モック化するための呼び出しクラス public class MockableInvoker { // モック化したいメソッドを呼び出す仮想メソッドを定義 public virtual IEnumerable<object> GetElements(ILibraryClass obj) { // 拡張メソッドを呼び出し return obj.GetElements(); } } // テスト対象クラスの利用例 public class SampleClass { private MockableInvoker Invoker; public SampleClass(MockableInvoker invoker = null) { Invoker = invoker ?? new MockableInvokder(); } public void GetElementCount(ILibraryClass obj) { var specifiedElements = Invoker.GetElements(obj); return specifiedElements.Count; } } // テストコード例 using Moq; [TestClass] public class SampleClassTests { [TestMethod] public void FuncTest() { // モック化 var invokerMock = new Mock<MockableInvoker>(); var expectModels = new Model[]{ new Model(), new Model(), new Model() }; invokerMock.Setup(m => m.GetElements).Returns(expectModels); var obj = new LibraryClass(); var sample = new SampleClass(invokerMock); var count = sample.GetElementCount(obj); Assert.AreEqual(3, count); } } 手法3 モック用インターフェース定義 単体テスト用に拡張メソッドの対象を派生したインターフェースまたは抽象クラスを定義して、拡張メソッド内で判断、分岐します。 拡張メソッド内でこのインターフェースにキャストできた場合 単体テスト実行中であり、インターフェースを使ってモックに期待値がセットアップされているはずなのでそれを返す。 拡張メソッド内でこのインターフェースにキャストできない場合 本体コードが実行中であり、拡張メソッド内に実装した処理を実行する。 評価項目 評価 理由 拡張メソッド ? モックを意識した実装が必要。 テスト対象 ⭕ 変更不要。 テストコード ⭕ 違和感なし。 テスト対象クラスを変更する必要が無く、拡張メソッドも1行追加するだけです。 // モック化するためのインターフェース定義 // 拡張メソッドの対象が実クラスの場合は抽象クラス定義 public interface ILibraryClassMock : ILibraryClass { // モック化したいメソッドを定義 IEnumerable<object> GetElements(); } // 拡張メソッド public static class LibraryClassExtensions { public static IEnumerable<object> GetElements(this ILibraryClass obj) { // モックの場合はセットアップされた期待値を返す if (obj is ILibraryClassMock mock) return mock.GetElements(); // 本来の拡張メソッドの処理 ... } } // テスト対象クラスの利用例 public class SampleClass { public void GetElementCount(ILibraryClass obj) { var specifiedElements = obj.GetElements(); return specifiedElements.Count; } } // テストコード例 using Moq; [TestClass] public class SampleClassTests { [TestMethod] public void FuncTest() { var classMock = new Mock<ILibraryClassMock>(); var expectModels = new Model[]{ new Model(), new Model(), new Model() }; classMock.Setup(m => m.GetElements).Returns(expectModels); var obj = new LibraryClass(); var sample = new SampleClass(); var count = sample.GetElementCount(obj); Assert.AreEqual(3, count); } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む