具有只读属性的模拟方法返回值

时间:2017-05-09 00:25:24

标签: c# unit-testing automated-tests moq

我希望测试查询第三方库的方法。该库返回一个具有IReadOnlyCollection属性的对象。

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

我使用Moq来模拟我调用的服务的界面,但我无法创建模拟的返回值,因为我无法设置属性。

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

生成返回值以测试方法的最佳方法是什么?使用new属性包装对象以隐藏基础不起作用。

3 个答案:

答案 0 :(得分:2)

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

public interface IHitService
{
    IHit GetHit();
}

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

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

答案 1 :(得分:2)

您可以使用允许您更改具体对象行为的单元测试框架,例如在这种情况下我使用Typemock Isolator尝试解决您的问题,它允许您更改结果的返回值财产所以可以&#34;设置&#34;在不更改代码或添加额外代码的情况下进行测试:

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类,并将Result属性的返回值修改为我创建的字符串列表。

答案 2 :(得分:1)

一般情况下,如果您无法更改第三方代码,请为其构建适配器并使用您自己的抽象: -

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