如何模拟Excel VSTO插件中的行? [英] How to mock rows in a Excel VSTO plugin?

查看:66
本文介绍了如何模拟Excel VSTO插件中的行?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在新的Range的行中放置模拟的Range(其中包含具有值的单元格).但是,当我尝试从Range访问特定元素时,会引发异常.

I am trying to put a mocked Range (which contains cells with values) inside the rows of a new Range. But when I try to access a specific element from the Range, a exception is thrown.

我已经尝试了一切,有人知道我在做什么错吗?

I've tried everything, does anyone have a idea what I am doing wrong here?

例外

消息:测试方法xxx.MockUtilsTest.MockRowsTest抛出异常: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:无法应用 用[]索引到'Castle.Proxies.RangeProxy'类型的表达式

Message: Test method xxx.MockUtilsTest.MockRowsTest threw exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot apply indexing with [] to an expression of type 'Castle.Proxies.RangeProxy'

测试

[TestMethod]
public void MockRowsTest()
{
    var row1 = MockUtils.MockCells("test_row_1", "test_row_1");
    var row2 = MockUtils.MockCells("test_row_2", "test_row_2");
    var range = MockUtils.MockRows(row1, row2);

    Assert.IsNotNull(range);
    Assert.AreEqual(2, range.Count);
    Assert.IsNotNull(range.Rows);
    Assert.AreEqual(2, range.Rows.Count);
    Assert.AreSame(row1, range.Rows[1].Cells[1]); // exception is thrown here
    Assert.AreSame(row2, range.Rows[2].Cells[1]);
    Assert.AreEqual("test_row_1", range.Rows[1].Cells[1].Value2);
    Assert.AreEqual("test_row_2", range.Rows[2].Cells[1].Value2);
}

MockUtils

public static Range MockCellValue2(Object value)
{
    var cell = new Moq.Mock<Range>();
    cell.Setup(c => c.Value2).Returns(value);

    return cell.Object;
}

public static Range MockCells(params Object[] values)
{
    var cells = new Moq.Mock<Range>();
    for (int i = 0; i < values.Length; i++)
    {
        var cell = MockCellValue2(values[i]);
        cells.SetupGet(c => c[i + 1, Moq.It.IsAny<Object>()]).Returns(cell);
    }

    var row = new Moq.Mock<Range>();
    row.SetupGet(r => r.Cells).Returns(cells.Object);
    row.SetupGet(r => r.Count).Returns(values.Length);

    return row.Object;
}

public static Range MockRows(params Range[] rows)
{
    var mergedRows = MergeRanges(rows);
    var range = new Moq.Mock<Range>();
    range.SetupGet(r => r.Count).Returns(rows.Length);
    range.SetupGet(r => r.Rows).Returns(() => mergedRows);
    range.Setup(r => r.GetEnumerator()).Returns(rows.GetEnumerator());

    return range.Object;
}

public static Range MergeRanges(params Range[] ranges)
{
    var range = new Moq.Mock<Range>();
    for (int i = 0; i < ranges.Length; i++)
    {
        range.SetupGet(r => r[i + 1, Moq.It.IsAny<Object>()]).Returns(ranges[i]);
    }

    range.SetupGet(r => r.Count).Returns(ranges.Length);
    range.Setup(r => r.GetEnumerator()).Returns(ranges.GetEnumerator());

    return range.Object;
}

推荐答案

Range的索引器返回一个动态对象,这是问题的根源.

The indexer of Range returns a dynamic object, this is the source of your problem.

最小起订量使用Castle Dynamic proxy生成伪造的对象,Castle.Proxies.RangeProxy是您所生成的类.由于此对象不是COM对象,因此正在调用C#运行时绑定程序的处理.运行时绑定程序解析类型并寻找indexer方法,但是他无法解析它,因为生成的类没有它.

Moq use Castle Dynamic proxy to generate fake objects, Castle.Proxies.RangeProxy is the generated class in your case. Since this object is not a COM object the processing of those the C# Runtime binder is being called. The Runtime binder resolves the type and looking for the indexer method, but he failed to resolve it because the generated class don't have it.

解决您的最简单方法是将索引器结果返回到严格的Range局部变量:

The easiest way to solve your is to return the indexer result to a strict Range local variable:

然后您的测试将失败,因为range.Rows[1]等于row1 ...

Then your test will fail because range.Rows[1] is equals to row1...

因此将您的测试代码更改为:

So change your test code to:

[TestMethod]
public void MockRowsTest()
{
    var row1 = MockUtils.MockCells("test_row_1", "test_row_1");
    var row2 = MockUtils.MockCells("test_row_2", "test_row_2");
    var range = MockUtils.MockRows(row1, row2);

    Assert.IsNotNull(range);
    Assert.AreEqual(2, range.Count);
    Assert.IsNotNull(range.Rows);
    Assert.AreEqual(2, range.Rows.Count);
    Range x = range.Rows[1];
    Range y = range.Rows[2];
    var xCell = x.Cells[1];
    var yCell = y.Cells[1];
    Assert.AreSame(row1, x); 
    Assert.AreSame(row2, y);
    Assert.AreEqual("test_row_1", xCell.Value2);
    Assert.AreEqual("test_row_2", yCell.Value2);
}

上述UT将通过测试. IMO,您应该中断对原子OPS(多行)和方法"的聚合调用,而不是因为它将通过测试,因为它将使您的代码成为调试友好的代码.(我称您的代码为第11条规则"从编写之时起,它将至少被读取10次以上.因此,让编译器删除可传递的局部变量,并将其设置为调试友好的代码.).

The above UT will pass the test. IMO you should to break your aggregation calls to "atomic OPS(multi line) and methods" not because it will pass the test, because it will make your code a debug friendly code.(I call that "the 11TH rule" where your code will be read a least 10 more times from the time it was written... So let the compiler to remove the transitive local variables and make your it a debug friendly code..).

在这里您可以阅读简单而简短的内容有关动态如何在c#中工作的说明.

在这里您可以阅读有关Castle Dynamic Proxy的更多信息.

顺便说一句;您还可以:

BTW; you also can do:

Range x = range.Rows[1].Cells;
var str = x[1].Value2;

接收值

这篇关于如何模拟Excel VSTO插件中的行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆