MoqでIMemoryCache
をモックしようとしています。私はこのエラーを受け取ります:
タイプ 'System.NotSupportedException'の例外がMoq.dllで発生しましたが、ユーザーコードでは処理されませんでした
追加情報:式は、モックされたオブジェクトに属していないメソッドを参照します:x => x.Get <String>(It.IsAny <String>())
私のあざけるコード:
_namespace Iag.Services.SupplierApiTests.Mocks
{
public static class MockMemoryCacheService
{
public static IMemoryCache GetMemoryCache()
{
Mock<IMemoryCache> mockMemoryCache = new Mock<IMemoryCache>();
mockMemoryCache.Setup(x => x.Get<string>(It.IsAny<string>())).Returns("");<---------- **ERROR**
return mockMemoryCache.Object;
}
}
}
_
なぜそのエラーが出るのですか?
これはテスト中のコードです:
_var cachedResponse = _memoryCache.Get<String>(url);
_
ここで__memoryCache
_はIMemoryCache
タイプです
上記の_memoryCache.Get<String>(url)
をモックしてnullを返すにはどうすればよいですか?
編集:_memoryCache.Set<String>(url, response);
に対して同じことをするにはどうすればよいですか?何が返されてもかまいません。モックにメソッドを追加するだけで、呼び出されてもスローされません。
私が試したこの質問の答えに行く:
_mockMemoryCache
.Setup(m => m.CreateEntry(It.IsAny<object>())).Returns(null as ICacheEntry);
_
これは、memoryCache拡張機能では、CreateEntry
内でSet
を使用していることを示しているためです。しかし、「オブジェクト参照がオブジェクトのインスタンスに設定されていません」というエラーが発生しています。
MemoryCacheExtensions.cs のソースコードによると、
_Get<TItem>
_拡張メソッドは、以下を使用します
_public static TItem Get<TItem>(this IMemoryCache cache, object key) {
TItem value;
cache.TryGetValue<TItem>(key, out value);
return value;
}
public static bool TryGetValue<TItem>(this IMemoryCache cache, object key, out TItem value) {
object result;
if (cache.TryGetValue(key, out result)) {
value = (TItem)result;
return true;
}
value = default(TItem);
return false;
}
_
基本的には TryGetValue(Object, out Object)
メソッドを使用していることに注意してください。
Moqを使用して拡張メソッドをモックすることができない場合は、拡張メソッドによってアクセスされるインターフェースメンバーをモックしてみてください。
MoqのクイックスタートMockMemoryCacheService
を更新して、テスト用のTryGetValue
メソッドを適切に設定します。
_public static class MockMemoryCacheService {
public static IMemoryCache GetMemoryCache(object expectedValue) {
var mockMemoryCache = new Mock<IMemoryCache>();
mockMemoryCache
.Setup(x => x.TryGetValue(It.IsAny<object>(), out expectedValue))
.Returns(true);
return mockMemoryCache.Object;
}
}
_
コメントから
(Getの代わりに)
TryGetValue
をモックする場合、out
パラメーターは、宣言されていなくてもobject
として宣言する必要があります。例えば:
_int expectedNumber = 1; object expectedValue = expectedNumber.
_これを行わないと、同じ名前のテンプレート化された拡張メソッドと一致します。
これは、memoryCache.Get<String>(url)
をモックしてnull
を返すように変更したサービスを使用した例です
_[TestMethod]
public void _IMemoryCacheTestWithMoq() {
var url = "fakeURL";
object expected = null;
var memoryCache = MockMemoryCacheService.GetMemoryCache(expected);
var cachedResponse = memoryCache.Get<string>(url);
Assert.IsNull(cachedResponse);
Assert.AreEqual(expected, cachedResponse);
}
_
このような_Set<>
_拡張メソッドにも同じプロセスを適用できます。
_public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value) {
var entry = cache.CreateEntry(key);
entry.Value = value;
entry.Dispose();
return value;
}
_
このメソッドはCreateEntry
メソッドを利用します。これは、同様に作用するICacheEntry
を返します。したがって、次の例のように、モックされたエントリを返すようにモックを設定します。
_[TestMethod]
public void _IMemoryCache_Set_With_Moq() {
var url = "fakeURL";
var response = "json string";
var memoryCache = Mock.Of<IMemoryCache>();
var cachEntry = Mock.Of<ICacheEntry>();
var mockMemoryCache = Mock.Get(memoryCache);
mockMemoryCache
.Setup(m => m.CreateEntry(It.IsAny<object>()))
.Returns(cachEntry);
var cachedResponse = memoryCache.Set<string>(url, response);
Assert.IsNotNull(cachedResponse);
Assert.AreEqual(response, cachedResponse);
}
_
Welrockenが指摘したように、モックしようとしているインターフェイスにGet
メソッドはありません。 Nkosiは、ほとんどの人がIMemoryCache
を使用する典型的な使用法である拡張メソッドのソースコードを参考にリンクしています。基本的に、すべての拡張メソッドは、実行中にどこかで3つのインターフェイスメソッドの1つを呼び出します。
何が起こっているのかをすばやく簡単に調べる方法は、モックされた3つのインターフェイスメソッドすべてにコールバックを設定し、ブレークポイントを挿入することです。
テストターゲットメソッドがGet
を呼び出していると想定して、Getメソッドの1つを具体的に模擬するには、次のようにその結果を模擬できます。
delegate void OutDelegate<TIn, TOut>(TIn input, out TOut output);
[Test]
public void TestMethod()
{
// Arrange
var _mockMemoryCache = new Mock<IMemoryCache>();
object whatever;
_mockMemoryCache
.Setup(mc => mc.TryGetValue(It.IsAny<object>(), out whatever))
.Callback(new OutDelegate<object, object>((object k, out object v) =>
v = new object())) // mocked value here (and/or breakpoint)
.Returns(true);
// Act
var result = _target.GetValueFromCache("key");
// Assert
// ...
}
編集:私はセッターをモックする方法の例を この答え に追加しました。
MemoryCacheEntryOptions and .AddExpirationToken
を使用してセットを呼び出す場合は、トークンのリストを持つエントリも必要です。
これは上記の@Nkosiの回答への追加です。例:
// cache by filename: https://jalukadev.blogspot.com/2017/06/cache-dependency-in-aspnet-core.html
var fileInfo = new FileInfo(filePath);
var fileProvider = new PhysicalFileProvider(fileInfo.DirectoryName);
var options = new MemoryCacheEntryOptions();
options.AddExpirationToken(fileProvider.Watch(fileInfo.Name));
this.memoryCache.Set(key, cacheValue, options);
モックには以下を含める必要があります。
// https://github.com/aspnet/Caching/blob/45d42c26b75c2436f2e51f4af755c9ec58f62deb/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs
var cachEntry = Mock.Of<ICacheEntry>();
Mock.Get(cachEntry).SetupGet(c => c.ExpirationTokens).Returns(new List<IChangeToken>());
var mockMemoryCache = Mock.Get(memoryCache);
mockMemoryCache
.Setup(m => m.CreateEntry(It.IsAny<object>()))
.Returns(cachEntry);
_Microsoft.Extensions.Caching.Memory.IMemoryCache
_インターフェイスにはMicrosoft.Extensions.Caching.Memory.IMemoryCache.Get(object)
メソッドはありません。あなたが使おうとするものは_Microsoft.Extensions.Caching.Memory.CacheExtensions
_にあります。これらの回答を見て、間接的に質問に答えることができます。
また、後で使用するためにMoqを構成する方法にも注意する必要があります。
コードは、Getメソッドが文字列を返し、文字列パラメーターを受け取ることを示しています。テスト構成がテスト全体を通してそれに従っている場合は、問題ありません。ただし、宣言により、Getメソッドはオブジェクトをキーとして受け取ります。したがって、パラメーター述語のコードはIt.IsAny<object>()
になります。
2つ目は、nullを返したい場合は、関数が実際に返す型にキャストする必要があることです(例:.Returns((string)null)
)。これは Moq.Language.IReturns.Returns に他のオーバーロードがあり、コンパイラが参照しようとしているものを決定できないためです。