【问题标题】:Mocking Redlock.CreateAsync does not return mocked object模拟 Redlock.CreateAsync 不返回模拟对象
【发布时间】:2021-06-07 10:58:54
【问题描述】:

我正在尝试模拟 Redlock

我有下面的测试

using Moq;
using RedLockNet;
using System;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace RedLock.Tests 
{
   public class RedLockTests 
   {
       [Fact]
       public async Task TestMockingOfRedlock()
       {
            var redLockFactoryMock = new Mock<IDistributedLockFactory>();

            var mock = new MockRedlock();
            redLockFactoryMock.Setup(x => x.CreateLockAsync(It.IsAny<string>(),
                It.IsAny<TimeSpan>(), It.IsAny<TimeSpan>(),
                It.IsAny<TimeSpan>(), It.IsAny<CancellationToken>()))
            .ReturnsAsync(mock);

             var sut = new TestRedlockHandler(redLockFactoryMock.Object);

             var data = new MyEventData();
             await sut.Handle(data);
        }
    }
}

MockRedlock 是一个实现 IRedLock 的简单模拟类

public class MockRedlock: IRedLock
{
    public void Dispose()
    {
        
    }

    public string Resource { get; }
    public string LockId { get; }
    public bool IsAcquired => true;
    public RedLockStatus Status => RedLockStatus.Acquired;
    public RedLockInstanceSummary InstanceSummary => new RedLockInstanceSummary();
    public int ExtendCount { get; }
}

await sut.Handle(data); 是对单独事件类的调用

我在下面展示了这个。这已经被简化了,但是使用下面的代码和上面的测试可以重现空引用错误

public class MyEventData
{
    public string Id { get; set; }

    public MyEventData()
    {
        Id = Guid.NewGuid().ToString();
    }
}


public class TestRedlockHandler
{
    private IDistributedLockFactory _redLockFactory;

    public TestRedlockHandler(IDistributedLockFactory redLockFactory)
    {
        _redLockFactory = redLockFactory;
    }

    public async Task Handle(MyEventData data)
    {
        var lockexpiry = TimeSpan.FromMinutes(2.5);
        var waitspan = TimeSpan.FromMinutes(2);
        var retryspan = TimeSpan.FromSeconds(20);
        using (var redlock =
            await _redLockFactory.CreateLockAsync(data.Id.ToString(), lockexpiry, waitspan, retryspan, null))
        {
            if (!redlock.IsAcquired)
            {
                string errorMessage =
                    $"Did not acquire Lock on Lead {data.Id.ToString()}.  Aborting.\n " +
                    $"Acquired{redlock.InstanceSummary.Acquired} \n " +
                    $"Error{redlock.InstanceSummary.Error} \n" +
                    $"Conflicted {redlock.InstanceSummary.Conflicted} \n" +
                    $"Status {redlock.Status}";
                throw new Exception(errorMessage);
            }
        }
    }
}

当我尝试调用它时,我希望我的对象被返回,但我却得到了 null

在线if (!redlock.IsAcquired) redLock 为空

缺少什么?

【问题讨论】:

    标签: c# unit-testing mocking redlock.net


    【解决方案1】:

    CreateLockAsync的定义

    /// <summary>
    /// Gets a RedLock using the factory's set of redis endpoints. You should check the IsAcquired property before performing actions.
    /// Blocks and retries up to the specified time limits.
    /// </summary>
    /// <param name="resource">The resource string to lock on. Only one RedLock should be acquired for any given resource at once.</param>
    /// <param name="expiryTime">How long the lock should be held for.
    /// RedLocks will automatically extend if the process that created the RedLock is still alive and the RedLock hasn't been disposed.</param>
    /// <param name="waitTime">How long to block for until a lock can be acquired.</param>
    /// <param name="retryTime">How long to wait between retries when trying to acquire a lock.</param>
    /// <param name="cancellationToken">CancellationToken to abort waiting for blocking lock.</param>
    /// <returns>A RedLock object.</returns>
    Task<IRedLock> CreateLockAsync(string resource, TimeSpan expiryTime, TimeSpan waitTime, TimeSpan retryTime, CancellationToken? cancellationToken = null);
    

    需要一个可以为空的CancellationToken

    CancellationToken? cancellationToken = null
    

    但是mock的设置使用

    It.IsAny<CancellationToken>() //NOTE CancellationToken instead of CancellationToken?
    

    因为设置需要不可为空的结构,但是当调用可空的 CancellationToken? 时,即使它为空也会传递,

    默认情况下,mock 将返回 null,因为设置与实际调用的不匹配。

    一旦使用了正确的类型,工厂就能够返回所需的模拟

    //...
    
    redLockFactoryMock
        .Setup(x => x.CreateLockAsync(It.IsAny<string>(),
                It.IsAny<TimeSpan>(), It.IsAny<TimeSpan>(),
                It.IsAny<TimeSpan>(), It.IsAny<CancellationToken?>()))
        .ReturnsAsync(mock);
    
    //...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多