1.21 jigowatts

Great Scott!

【C#】App.configにJSONで設定値を定義

概要

とあるクライアントアプリでリスト形式の設定値を外部ファイル化したいよねって話があって、App.configにaddタグで大量に定義するか悩んで、Json.NETを使ってJSON形式で設定してみることにしました。

addタグたくさん書くのは気が引ける。

<appSettings>
  <!--users-->
  <add key="1" value="aaa" />
  <add key="2" value="bbb" />
  <add key="3" value="ccc" />
</appSettings>
環境

Visual Studio Community 2017

実装

パッケージマネージャーコンソールよりJson.NETをインストールしておきます。

PM> Install-Package Newtonsoft.Json
App.config

addの定義は一つだけ。あれ、読みにくい?

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
    </startup>
    <appSettings>
      <add key="users" value='[
           {"ID":"1","Name":"aaa"},
           {"ID":"2","Name":"bbb"},
           {"ID":"3","Name":"ccc"},
           ]'/>
    </appSettings>
</configuration>
Program.cs

とは言え、コンフィグから値を読み出す処理はkey一つでいいのでシンプル。addタグを連ねてたら動的に読んだりするし。

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Configuration;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            var users = ConfigurationManager.AppSettings["users"];

            var paramList = JsonConvert.DeserializeObject<List<User>>(users);

            foreach (var param in paramList)
            {
                Console.WriteLine(param.ID + ":" + param.Name);
            }

            Console.ReadKey(true);
        }
    }

    class User
    {
        public String ID { get; set; }
        public String Name { get; set; }
    }
}

実行結果

取れてるっぽいので、あとはデータソースに突っ込むだけ!

1:aaa
2:bbb
3:ccc

【.NET Core】MoqのQuickstartをやってみる(Miscellaneous)

概要

Miscellaneousってなんだろう?と思って調べたら「その他」とか「雑多な」とかって意味らしい。略してmisc。

github.com

環境
  • macOS Sierra バージョン 10.12.6
  • .NET Core 2.0
  • Moq 4.7.137

テストコード

呼び出し回数に応じて戻り値や例外投げる方法とprotectedメンバーのセットアップについてですね。思ってたより雑多感はない。

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Moq.Protected;
using MoqQuickstart;

namespace MoqQuickstart.Tests
{
    [TestClass]
    public class Miscellaneous
    {
        [TestMethod]
        public void setting_up_a_member_to_return_different_values_throw_exceptions_on_sequential_calls(){
            var mock = new Mock<IFoo>();
            mock.SetupSequence(f => f.GetCount())
                .Returns(3)  // will be returned on 1st invocation
                .Returns(2)  // will be returned on 2nd invocation
                .Returns(1)  // will be returned on 3rd invocation
                .Returns(0)  // will be returned on 4th invocation
                .Throws(new InvalidOperationException());  // will be thrown on 5th invocation

            var foo = mock.Object;
            var first = foo.GetCount();
            var second = foo.GetCount();
            var third = foo.GetCount();
            var fourth = foo.GetCount();

            Assert.AreEqual(3,first);
            Assert.AreEqual(2,second);
            Assert.AreEqual(1,third);
            Assert.AreEqual(0,fourth);
            try
            {
                var fifth = foo.GetCount();
                Assert.Fail();
            }
            catch(InvalidOperationException)
            {
                Assert.IsTrue(true);
            }
        }

        [TestMethod]
        public void setting_expectations_for_protected_members()
        {
            var mock = new Mock<CommandBase>();
            mock.Protected()
                .Setup<int>("Execute")
                .Returns(5);

            mock.Protected()
                .Setup<string>("Execute", ItExpr.IsAny<string>())
                .Returns("foo");

            Assert.AreEqual(5, mock.Object.Processing());

            Assert.AreEqual("foo", mock.Object.Processed("bar"));
        }
    }
}

QuickstartのページにはCommandBaseクラスの記述がないので、これも追加で。

using System;

namespace MoqQuickstart
{
    public class CommandBase
    {
        public int Processing()
        {
            return Execute();
        }

        public string Processed(string s)
        {
            return Execute(s);
        }

        protected virtual int Execute()
        {
            return 0;
        }

        protected virtual string Execute(string s)
        {
            return "fix:" + s;
        }
    }
}

テストを実行してみましょう。

$ dotnet test --filter "FullyQualifiedName~MoqQuickstart.Tests.Miscellaneous"
Build started, please wait...
Build completed.

Test run for /Users/soil/src/github/unit-testing-using-dotnet-test/PrimeService.Tests/bin/Debug/netcoreapp2.0/PrimeService.Tests.dll(.NETCoreApp,Version=v2.0)
Microsoft (R) Test Execution Command Line Tool Version 15.3.0-preview-20170628-02
Copyright (c) Microsoft Corporation.  All rights reserved.

テスト実行を開始しています。お待ちください...

テストの合計数: 2。成功: 2。失敗:0。スキップ: 0。
テストの実行に成功しました。
テスト実行時間: 2.8108 秒

ミスレニアス?ミセレイニアス?
Miscellaneousでしょう。

【.NET Core】MoqのQuickstartをやってみる(Verification)

概要

MoqのQuickstartをやってみる。今回はVerifyメソッドです。

github.com

環境
  • macOS Sierra バージョン 10.12.6
  • .NET Core 2.0
  • Moq 4.7.137

テストコード

こんな感じかなぁ。

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using MoqQuickstart;

namespace MoqQuickstart.Tests
{
    [TestClass]
    public class Verification
    {
        [TestMethod]
        public void verify()
        {
            var mock = new Mock<IFoo>();
            mock.Object.DoSomething("ping");
            mock.Verify(foo => foo.DoSomething("ping"));
        }

        [TestMethod]
        public void verify_with_custom_error_message_for_failure()
        {
            var mock = new Mock<IFoo>();
            mock.Verify(foo => foo.DoSomething("ping"), "When doing operation X, the service should be pinged always");
        }

        [TestMethod]
        public void method_should_never_be_called()
        {
            var mock = new Mock<IFoo>();
            mock.Verify(foo => foo.DoSomething("ping"), Times.Never());
        }

        [TestMethod]
        public void called_at_least_once()
        {
            var mock = new Mock<IFoo>();
            mock.Object.DoSomething("ping");
            mock.Verify(foo => foo.DoSomething("ping"), Times.AtLeastOnce());
        }

        [TestMethod]
        public void verify_getter_invocation_regardless_of_value()
        {
            var mock = new Mock<IFoo>();
            var name = mock.Object.Name;
            mock.VerifyGet(foo => foo.Name);
        }

        [TestMethod]
        public void verify_setter_invocation_regardless_of_value()
        {
            var mock = new Mock<IFoo>();
            mock.Object.Name = "foo";
            mock.VerifySet(foo => foo.Name);
        }

        [TestMethod]
        public void verify_setter_called_with_specific_value()
        {
            var mock = new Mock<IFoo>();
            mock.Object.Name = "foo";
            mock.VerifySet(foo => foo.Name ="foo");
        }

        [DataTestMethod]
        [DataRow(1)]
        [DataRow(3)]
        [DataRow(5)]
        public void verify_setter_with_an_argument_matcher(int value)
        {
            var mock = new Mock<IFoo>();
            mock.Object.Value = value;
            mock.VerifySet(foo => foo.Value = It.IsInRange(1, 5, Range.Inclusive));
        }
    }
}

テストを実行してみましょう。
verify_with_custom_error_message_for_failureでちゃんとエラーメッセージも表示されていますね!

$ dotnet test --filter "FullyQualifiedName~MoqQuickstart.Tests.Verification"
Build started, please wait...
Build completed.

Test run for /Users/soil/src/github/unit-testing-using-dotnet-test/PrimeService.Tests/bin/Debug/netcoreapp2.0/PrimeService.Tests.dll(.NETCoreApp,Version=v2.0)
Microsoft (R) Test Execution Command Line Tool Version 15.3.0-preview-20170628-02
Copyright (c) Microsoft Corporation.  All rights reserved.

テスト実行を開始しています。お待ちください...
失敗   MoqQuickstart.Tests.Verification.verify_with_custom_error_message_for_failure
エラー メッセージ:
 Test method MoqQuickstart.Tests.Verification.verify_with_custom_error_message_for_failure threw exception: 
Moq.MockException: When doing operation X, the service should be pinged always
Expected invocation on the mock at least once, but was never performed: foo => foo.DoSomething("ping")
No setups configured.
No invocations performed.
スタック トレース:
    at Moq.Mock.ThrowVerifyException(MethodCall expected, IEnumerable`1 setups, IEnumerable`1 actualCalls, Expression expression, Times times, Int32 callCount)
   at Moq.Mock.VerifyCalls(Interceptor targetInterceptor, MethodCall expected, Expression expression, Times times)
   at Moq.Mock.Verify[T,TResult](Mock`1 mock, Expression`1 expression, Times times, String failMessage)
   at Moq.Mock`1.Verify[TResult](Expression`1 expression, String failMessage)
   at MoqQuickstart.Tests.Verification.verify_with_custom_error_message_for_failure() in /Users/soil/src/github/unit-testing-using-dotnet-test/PrimeService.Tests/Verification.cs:line 23


テストの合計数: 10。成功: 9。失敗:1。スキップ: 0
テストの実行に失敗しました。
テスト実行時間: 2.3554 秒

はい、次!