模拟方法 return 只读值 属性

Mock method return value with read only property

我想测试一个查询第三方库的方法。库 return 是一个带有 IReadOnlyCollection 属性 的对象。

没有构造函数来设置 属性 的值,对象也没有可供我模拟的接口。

我已经使用 Moq 模拟我调用的服务的接口,但我无法创建模拟的 return 值,因为我无法设置 属性 .

public interface IHitService {
    public Hit GetHit();
}

public class Hit {
    public Hit() {
    }

    public IReadOnlyCollection<string> Result { get; }
}

public class TestingClass {
    public void MyTest() {
        Hit hit = new Hit() {
            // cannot set this property
            Result = new List<string>() { "hello","goodbye" };
        }

        Mock<IHitService> service = new Mock<IHitService>();
        service.Setup(c => c.GetHit).Returns(hit);
    }
}

生成 return 值以测试我的方法的最佳方法是什么?用 new 属性 包裹对象以隐藏底座不起作用。

如果您需要测试第三方库,最好创建自己的抽象(接口)并在测试和实际代码中依赖它:

public interface IHitService
{
    IHit GetHit();
}

public interface IHit
{
    IReadOnlyCollection<string> Result { get; }
}

在您的应用程序代码中,您可以创建一个简单的包装器 class,它通过委托给具体的第三方 class 来实现 IHit。现在您可以根据需要通过模拟来测试界面。

一般来说,如果您不能更改第 3 方代码,请为其构建一个适配器并使用您自己的抽象 :-

public interface IHit
{
    IReadOnlyCollection<string> Result { get; }
}

public interface IHitService
{
    IHit GetHit();
}

public class HitAdapter : IHit
{
    private Hit _hit;

    public HitAdapter(Hit hit)
    {
        _hit = hit;
    }

    public IReadOnlyCollection<string> Result => _hit.Result;
}


public class TestingClass
{
    public void MyTest()
    {
        var hitMock =  new Mock<IHit>();
        hitMock.Setup(c => c.Result).Returns<IReadOnlyCollection<string>>(x => new List<string>() {"hello", "goodbye"});
        var service = new Mock<IHitService>();
        service.Setup(c => c.GetHit()).Returns<IHit>(x => hitMock.Object);
    }
}

您可以使用允许您更改具体对象行为的单元测试框架,例如在这种情况下,我使用 Typemock Isolator 来尝试解决您的问题,它允许您更改 return 结果的值 属性 因此可以 "set" 进行测试,而无需更改代码或添加额外代码:

public void TestMethod1()
{
    List<string> s = new List<string> { "sfas", "asfsa", "blbba" };
    var hit = Isolate.Fake.NextInstance<Hit>();

    Isolate.WhenCalled(() => hit.Result).WillReturnCollectionValuesOf(s);
}

在此测试中,我模拟了 Hit class 并将结果 属性 的 return 值修改为我创建的字符串列表。