C# 模拟具有内部属性资源库的类

本文关键字:资源库 属性 内部 模拟 | 更新日期: 2023-09-27 18:31:57

我正在尝试构建一个单元测试。类位置在第三方库中实现。但是对于我的单元测试,我需要将 Size 属性设置为特定值。

public class Position
{
    private double _size;
    private double Size
    {
        get
        {
            return _size;
        }
        internal set
        {
            _size = value;
        }
    }
}

我读了这篇文章:如何为包含只读成员的接口创建单元测试存根?但无法弄清楚如何让它为我工作。

这是正在测试的类(只是一个简化的示例)。CalcPositionMetric() 方法中的 pos 参数必须是 Position 类型:

public class PositionMetrics
{
    public PositionMetrics()
    {}
    public double CalcPositionMetric(Position pos)
    {
        return 2 * pos.Size;
    }
}

这是我的单元测试的一部分:

using NUnit.Framework;
using NMock;
[TestFixture]
public class PositionUnitTests
{
    [Test]
    public void TestPosition()
    {
        Mock<Position> tmpPosMock   = mFactory.CreateMock<Position>();
        tmpPosMock.Expects.One.GetProperty(v => v.Size).WillReturn(7); /* !!! Exception !!! System.ArgumentException : mock object position has a getter for property Size, but it is not virtual or abstract */
        /* Execute Test with  tmpPositions*/
        PositionMetrics pm = new PositionMetrics();
        double result      = pm.CalcPositionMetric(tmpPosMock.MockObject)
        Assert.AreEqual(14, result);
    }
}

但正如你所看到的,我得到了一个例外。有人可以帮助我解决这个问题吗?也欢迎任何其他解决方案!

干杯康斯坦丁

C# 模拟具有内部属性资源库的类

更新问题的新答案 我建议您为此引入某种代理接口。请参阅下面的代码:

interface IPosition { 
    int Size { get; }
}
class Position { //in 3rd party lib
    public int Size {
        get { return 5; }
    }
}
class RealPosition : IPosition { //use this as your real object instead of using Position directly
    private Position position;
    public RealPosition(Position position) {
        this.position = position;
    }
    public int Size {
        get { return position.Size; }
    }
}
class MockPosition : IPosition { //use this for testing
    public int Size{ get; set; }
}
public class Program {
    static void Main(string[] args) {
        var pos = new MockPosition { Size = 7 };
        Console.WriteLine(Calc(pos));    //prints 14
        Console.ReadLine();
    }
    static int Calc(IPosition pos) { //change your method signature to work with interface
        return pos.Size * 2;
    }       
}

旧答案 如果类不sealed则不需要任何模拟库。只需对所需属性使用 new 修饰符,如下所示:

class Position {
    public int Size { get { return 5; } }
}
class MockPosition : Position {
    public new int Size { get; set; }
}
....
var mock= new MockPosition();
mock.Size = 7;

要在某种列表中使用这些项目,您必须像这样投射它们:

var items = new List<Position>();
for (int i = 0; i < 5; i++) {
      items.Add(new MockPosition { Size = i });
}
foreach (var item in items.Cast<MockPosition>()) {
       Console.Write("{0}'t", item.Size); //prints 0 1 2 3 4
}

如果它是密封的并且该属性不是虚拟的,那么您将不得不使用其他一些技术,Moq(我猜您正在使用)不允许这样做