wake-up-neo.net

Mock IMemoryCache mit Ausnahme des Typs "Moq"

Ich versuche, IMemoryCache mit Moq zu verspotten. Ich erhalte diesen Fehler:

Eine Ausnahme des Typs 'System.NotSupportedException' trat in der Moq.dll auf, wurde jedoch nicht im Benutzercode behandelt

Zusätzliche Informationen: Ausdruck verweist auf eine Methode, die nicht zum falschen Objekt gehört. X => x.Get <String> (It.IsAny <String> ())

Mein Spottcode:

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;
        }
    }
}

Warum erhalte ich diesen Fehler?

Dies ist der zu testende Code:

var cachedResponse = _memoryCache.Get<String>(url);

Wobei _memoryCache vom Typ IMemoryCache ist

Wie verspottet ich die _memoryCache.Get<String>(url) oben und lässt sie null zurückgeben?

Edit : Wie würde ich dasselbe tun, außer für _memoryCache.Set<String>(url, response);? Es macht mir nichts aus, was es zurückgibt, ich muss nur die Methode zum Mock hinzufügen, damit es nicht geworfen wird, wenn es aufgerufen wird.

Nach der Antwort auf diese Frage habe ich versucht:

mockMemoryCache
    .Setup(m => m.CreateEntry(It.IsAny<object>())).Returns(null as ICacheEntry);

In den memoryCache-Erweiterungen wird angezeigt, dass CreateEntry in Set verwendet wird. Es wird jedoch ein Fehler ausgegeben, wenn "Objektreferenz nicht auf eine Instanz eines Objekts festgelegt ist".

13

Laut Quellcode für MemoryCacheExtensions.cs ,

Die Erweiterungsmethode Get<TItem> verwendet Folgendes 

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;
}

Beachten Sie, dass im Wesentlichen die Methode TryGetValue(Object, out Object) verwendet wird. 

Da es nicht möglich ist, Erweiterungsmethoden mit Moq zu simulieren, versuchen Sie, die Schnittstellenmitglieder zu verspotten, auf die die Erweiterungsmethoden zugreifen.

Beziehen Sie sich auf Moqs Quickstart update MockMemoryCacheService, um die TryGetValue-Methode für den Test richtig einzurichten.

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;
    }
}

Aus den Kommentaren

Beachten Sie, dass beim Spott von TryGetValue (anstelle von Get) der out-Parameter Als object deklariert werden muss, auch wenn dies nicht der Fall ist. 

Zum Beispiel: 

int expectedNumber = 1; 
object expectedValue = expectedNumber. 

Wenn Sie dies nicht tun, stimmt es mit einer vorlagemäßigen Erweiterungsmethode des gleichen Namens Überein.

Hier ein Beispiel, in dem der modifizierte Dienst verwendet wird, um die memoryCache.Get<String>(url) zu verspotten und null zurückzugeben.

[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);
}

AKTUALISIEREN

Dasselbe Verfahren kann für die Erweiterungsmethode Set<> angewendet werden, die so aussieht.

public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value) {
    var entry = cache.CreateEntry(key);
    entry.Value = value;
    entry.Dispose();

    return value;
}

Diese Methode verwendet die CreateEntry-Methode, die eine ICacheEntry zurückgibt, auf die auch reagiert wird. Richten Sie das Mock so ein, dass es einen gespielten Eintrag zurückgibt, wie im folgenden Beispiel

[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);
}
25
Nkosi

Wenn Sie das Set mit einem MemoryCacheEntryOptions and .AddExpirationToken aufrufen, benötigen Sie den Eintrag auch, um eine Liste von Token zu erhalten.

Dies ist eine Ergänzung zu @ Nkosis Antwort oben. Beispiel:

// 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);

Der Schein muss enthalten:

// 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);
1
Aligned

In der Microsoft.Extensions.Caching.Memory.IMemoryCache-Schnittstelle gibt es keine Microsoft.Extensions.Caching.Memory.IMemoryCache.Get(object)-Methode. Die, die Sie verwenden möchten, befindet sich im Microsoft.Extensions.Caching.Memory.CacheExtensions. Sie können diese Antworten anzeigen, um Ihre Frage direkt zu beantworten.

Wie mache ich mit Moq eine Erweiterungsmethode?

Verspottende Erweiterungsmethoden mit Moq .

Sie sollten auch wissen, wie Sie Moq für spätere Verwendungen konfigurieren.

Ihr Code gibt an, dass die Get-Methode eine Zeichenfolge zurückgibt und einen Zeichenfolgenparameter verwendet. Wenn Ihre Testkonfiguration dem gesamten Test folgt, ist dies in Ordnung. Bei der Deklaration übernimmt die Get-Methode ein Objekt als Schlüssel. Ihr Code für das Parameter-Prädikat lautet also It.IsAny<object>()

Die zweite Sache ist, wenn Sie null zurückgeben möchten, sollten Sie dies in den Typ umwandeln, den Ihre Funktion tatsächlich zurückgibt (z. B. .Returns((string)null)). Dies liegt daran, dass es andere Überladungen für Moq.Language.IReturns.Returns gibt, und der Compiler kann nicht entscheiden, auf welche Sie sich beziehen möchten.

0
welrocken