【.NET Core】MoqのQuickstartをやってみる(Verification)
概要
MoqのQuickstartをやってみる。今回はVerify
メソッドです。
テストコード
こんな感じかなぁ。
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 秒
はい、次!
【.NET Core】MoqのQuickstartをやってみる(Callbacks)
概要
MoqのQuickstartをやってみる。Callback
メソッドについてです。
テストコード
Callback
メソッドの使い方に関しては特に何もないですね。そのまま使えばいいと思うmo(^q^)
using System; using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using MoqQuickstart; namespace MoqQuickstart.Tests { [TestClass] public class Callbacks { [TestMethod] public void callback() { var expected = 2; var mock = new Mock<IFoo>(); var calls = 0; mock.Setup(foo => foo.DoSomething("ping")) .Returns(true) .Callback(() => calls++); var f = mock.Object; f.DoSomething("ping"); f.DoSomething("pong"); f.DoSomething("ping"); Assert.AreEqual(expected, calls); } [TestMethod] public void access_invocation_arguments() { var expected = new List<string>(){"foo", "bar", "baz"}; var mock = new Mock<IFoo>(); var callArgs = new List<string>(); mock.Setup(foo => foo.DoSomething(It.IsAny<string>())) .Returns(true) .Callback((string s) => callArgs.Add(s)); var f = mock.Object; f.DoSomething("foo"); f.DoSomething("bar"); f.DoSomething("baz"); CollectionAssert.AreEqual(expected, callArgs); } [TestMethod] public void calalternate_equivalent_generic_method_syntax() { var expected = new List<string>(){"foo", "bar", "baz"}; var mock = new Mock<IFoo>(); var callArgs = new List<string>(); mock.Setup(foo => foo.DoSomething(It.IsAny<string>())) .Returns(true) .Callback<string>(s => callArgs.Add(s)); var f = mock.Object; f.DoSomething("foo"); f.DoSomething("bar"); f.DoSomething("baz"); CollectionAssert.AreEqual(expected, callArgs); } [TestMethod] public void access_arguments_for_methods_with_multiple_parameters() { var expected = new List<string>(){"foo", "bar", "baz"}; var mock = new Mock<IFoo>(); var callArgs = new List<string>(); mock.Setup(foo => foo.DoSomething(It.IsAny<int>(), It.IsAny<string>())) .Returns(true) .Callback<int, string>((i, s) => callArgs.Add(s)); var f = mock.Object; f.DoSomething(1, "foo"); f.DoSomething(2, "bar"); f.DoSomething(3, "baz"); CollectionAssert.AreEqual(expected, callArgs); } [TestMethod] public void callbacks_can_be_specified_before_and_after_invocation() { var mock = new Mock<IFoo>(); mock.Setup(foo => foo.DoSomething("ping")) .Callback(() => Console.WriteLine("Before returns")) .Returns(true) .Callback(() => Console.WriteLine("After returns")); var actual = mock.Object.DoSomething("ping"); Assert.IsFalse(actual); } } }
テストを実行してみましょう。callbacks_can_be_specified_before_and_after_invocation
はテスト失敗時に標準出力メッセージが確認できますね。
$ dotnet test --filter "FullyQualifiedName~MoqQuickstart.Tests.Callbacks" 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.Callbacks.callbacks_can_be_specified_before_and_after_invocation エラー メッセージ: Assert.IsFalse failed. スタック トレース: at MoqQuickstart.Tests.Callbacks.callbacks_can_be_specified_before_and_after_invocation() in /Users/soil/src/github/unit-testing-using-dotnet-test/PrimeService.Tests/Callbacks.cs:line 100 標準出力メッセージ: Before returns After returns テストの合計数: 5。成功: 4。失敗:1。スキップ: 0。 テストの実行に失敗しました。 テスト実行時間: 2.4901 秒
【.NET Core】MoqのQuickstartをやってみる(Properties)
概要
MoqのQuickstartをやってみる。次はプロパティの扱いについてです。
テストコード
書かれていない部分はこんな感じなのかなと考えながら書くのでミニヨクツクはず。
using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using MoqQuickstart; namespace MoqQuickstart.Tests { [TestClass] public class Properties { [TestMethod] public void setup() { var mock = new Mock<IFoo>(); mock.Setup(foo => foo.Name).Returns("bar"); Assert.AreEqual("bar",mock.Object.Name); } [TestMethod] public void auto_mocking_hierarchies() { var mock = new Mock<IFoo>(); mock.Setup(foo => foo.Bar.Baz.Name).Returns("baz"); Assert.AreEqual("baz",mock.Object.Bar.Baz.Name); } [TestMethod] public void expects_an_invocation_to_set_the_value_to_foo() { var mock = new Mock<IFoo>(MockBehavior.Strict); mock.SetupSet(foo => foo.Name = "foo"); mock.Object.Name = "foo"; } [TestMethod] public void or_verify_the_setter_directly() { var mock = new Mock<IFoo>(); mock.Object.Name = "foo"; mock.VerifySet(foo => foo.Name = "foo"); } [TestMethod] public void setup_a_property_so_that_it_will_automatically_start_tracking_its_value() { var mock = new Mock<IFoo>(); // start "tracking" sets/gets to this property mock.SetupProperty(f => f.Name); // alternatively, provide a default value for the stubbed property mock.SetupProperty(f => f.Name, "foo"); // Now you can do: IFoo foo = mock.Object; // Initial value was stored Assert.AreEqual("foo", foo.Name); // New value set which changes the initial value foo.Name = "bar"; Assert.AreEqual("bar", foo.Name); } [TestMethod] public void stub_all_properties_on_a_mock() { var mock = new Mock<IFoo>(); mock.SetupAllProperties(); mock.Object.Name = "foo"; mock.Object.Value = 123; Assert.AreEqual("foo", mock.Object.Name); Assert.AreEqual(123, mock.Object.Value); } } }
モックに対してプロパティを設定していく方法も何通りかあるんですね。
個人的にSetupSet
メソッドがよくわかりませんでした。 プロパティに"foo"が設定されることを期待しているということは、"foo"が設定されなかったらテスト失敗するのかと思っていたのですが、これだと失敗しません。
var mock = new Mock<IFoo>(); mock.SetupSet(foo => foo.Name = "foo"); mock.Object.Name = "bar";
リポジトリ内を検索してみたらMockBehavior.Strict
を渡しているサンプルを見つけました。厳密な振る舞いのモック?
var mock = new Mock<IFoo>(MockBehavior.Strict); mock.SetupSet(foo => foo.Name = "foo"); mock.Object.Name = "bar";
テストを実行してみると失敗します。
$ dotnet test --filter "FullyQualifiedName=MoqQuickstart.Tests.Properties.expects_an_invocation_to_set_the_value_to_foo" 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.Properties.expects_an_invocation_to_set_the_value_to_foo エラー メッセージ: Test method MoqQuickstart.Tests.Properties.expects_an_invocation_to_set_the_value_to_foo threw exception: Moq.MockException: IFoo.Name = "bar" invocation failed with mock behavior Strict. All invocations on the mock must have a corresponding setup. スタック トレース: at Moq.ExtractProxyCall.HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx) at Moq.Interceptor.Intercept(ICallContext invocation) at Castle.DynamicProxy.AbstractInvocation.Proceed() at MoqQuickstart.Tests.Properties.expects_an_invocation_to_set_the_value_to_foo() in /Users/soil/src/github/unit-testing-using-dotnet-test/PrimeService.Tests/Properties.cs:line 35 テストの合計数: 1。成功: 0。失敗:1。スキップ: 0。 テストの実行に失敗しました。 テスト実行時間: 2.4992 秒
他にはCallback
と組み合わせているコードがありました。
MockBehavior.Strict
にしてしまうとCallback
使うとき不便ですよね。
[TestMethod] public void setupSet_and_callback() { var mock = new Mock<IFoo>(); var when = true; var positive = false; mock.When(() => when).SetupSet(x => x.Name = "foo").Callback(() => positive = true); // Strictモードだと"baz"を設定した時点でテスト失敗 mock.Object.Name = "baz"; Assert.IsFalse(positive); mock.Object.Name = "foo"; Assert.IsTrue(positive); }
であればVerifySet
を使った方が、プロパティに"foo"が設定されることを期待しているケースでは直感的な感じ。
var mock = new Mock<IFoo>(); mock.Object.Name = "foo"; mock.VerifySet(foo => foo.Name = "foo");
手段が多いことはいいことですmo(^q^)