模拟 Workbook->Worksheets 时:'Microsoft.Office.Interop.Excel.Workbook' 类型的变量 'p' 从范围 '' 引用,但未定义

When mocking Workbook->Worksheets: variable 'p' of type 'Microsoft.Office.Interop.Excel.Workbook' referenced from scope '', but it is not defined

我刚刚开始学习如何使用 NUnit 进行单元测试,以及如何使用模拟来伪造我需要测试的对象。

目前我正在处理一个实现 VSTO Excel 插件的项目,我想开始对该项目使用单元测试。

所以我有一个函数,它将工作簿作为参数,returns 是我要处理的相关工作列表sheet。 我想测试这个功能。我 "just" 需要伪造作品 sheet 的名称,因为该函数只会遍历所有作品sheet 并检查它们的名称。

通过搜索 Internet 资源,我试图构建以下解决方案,试图伪造一个带有 Worksheet 列表的工作簿,名称列表 "SheetNames",其类型为 List:

        var fakeWorksheetList = new List<Excel.Worksheet>();
        foreach (string sheetName in SheetNames)
        {
            var sheet = Mock.Of<Excel.Worksheet>();
            sheet.Name = sheetName;
            fakeWorksheetList.Add(sheet);
        }

        var fakeWorkbook = new Mock<Excel.Workbook>();
        fakeWorkbook.Setup(p => p.Worksheets.GetEnumerator())
            .Returns(() => fakeWorksheetList.GetEnumerator());

但是当运行测试代码时,我得到以下错误信息:

variable "p" of type "Microsoft.Office.Interop.Excel.Workbook" referenced from scope "", but it is not defined

我做错了什么。为什么 Setup() 中的 lambda 表达式会给我这样的错误信息? 有没有更好的方法来伪造作品sheet的列表?

如果我表现出一些缺乏理解的地方,请原谅我,因为我真的只是从嘲讽的话题开始。

这是我要测试的功能,我需要在我的加载项中完成所有工作sheet我的加载项必须使用它:

    public static Dictionary<int, Excel.Worksheet> GetApplicableYearSheets(Excel.Workbook Workbook, int iCurrentYear = 0)
    {
        if (iCurrentYear <= 0)
            iCurrentYear = DateTime.Now.Year;

        int iFromYear = iCurrentYear - 2;
        Dictionary<int, Excel.Worksheet> YearSheets = new Dictionary<int, Excel.Worksheet>();

        for (int iNr = 1; iNr <= Workbook.Worksheets.Count; iNr++)
        {
            int iYear = 0;
            string sWorksheetName = Workbook.Worksheets[iNr].Name;
            if ((sWorksheetName.Trim().Length == 4) && (int.TryParse(sWorksheetName, out iYear)))
            {
                if ((iYear >= iFromYear) && (iYear <= iCurrentYear))
                    YearSheets.Add(iYear, Workbook.Worksheets[iNr]);
            }
        }

        return YearSheets;
    }

我要用于测试的 sheet 名称列表,例如:

        List<string> SheetNames = new List<string>()
        {
            "2012",
            "2013",
            "2014",
            "2015",
            "2016",
            "2017",
            "2018",
            "2019",
            "Test",
            "Spezialfälle"
        };

我的计划是测试使用模拟的 fakeWorkbook 调用此函数,例如通过以下方式:

Assert.That(InvoicingUtils.GetApplicableYearSheets(FakeWbFullNames, iCurrentYear: 2019), Has.Exactly(3).Items);

等等

我认为有一种更好更优雅的方式来编写模拟代码,但这对我有用。
请注意:
- 我使用 Moq 作为模拟框架。我相信你用的一样
- 我将您方法的 returning 类型更改为 IDictionary,因为我在 Dictionary

上遇到了一些问题

代码:
- 为了初始化你的测试 sheets 你可以使用一个循环。我只是让它变得简单,只创建了两个 sheet,没有任何循环。
- 我认为您只需在 workbook 对象上进行一些设置就可以让它工作,但我喜欢这种方式(只是个人喜好,没有别的
- 如您所见,整个逻辑是 sheets 对象上的 运行。基本上我从生产代码中得到一个索引,减去 -1 得到基于零的索引,将其保存到我的变量 sheetIndex 然后 return 从 worksheets 返回一个假的 sheet合集

using System.Collections.Generic;
using Excel = Microsoft.Office.Interop.Excel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

namespace ExcelMocking.Tests
{
    [TestClass]
    public class UnitTest1
    {
        private List<Excel.Worksheet> worksheets = new List<Excel.Worksheet>();

        [TestMethod]
        public void TestMethod1()
        {
            var sheet = new Mock<Excel.Worksheet>();
            sheet.SetupGet(w => w.Name).Returns("2012");
            worksheets.Add(sheet.Object);

            sheet = new Mock<Excel.Worksheet>();
            sheet.SetupGet(w => w.Name).Returns("2013");
            worksheets.Add(sheet.Object);

            var sheetIndex = 0;
            var sheets = new Mock<Excel.Sheets>();
            sheets.Setup(s => s.Count).Returns(() => worksheets.Count);
            sheets.Setup(s => s[It.IsAny<object>()]).Callback<object>((theSheetIndex) =>
            {
                // getting the real sheet index from the production code that starts from 1
                // and simple subtracting -1 to get zero based index
                sheetIndex = (int)theSheetIndex;
                sheetIndex--;
            }).Returns(() => worksheets[sheetIndex]);

            sheets.Setup(s => s.GetEnumerator()).Returns(() => worksheets.GetEnumerator());
            sheets.As<Excel.Sheets>().Setup(s => s.GetEnumerator()).Returns(() => worksheets.GetEnumerator());

            var workbook = new Mock<Excel.Workbook>();
            workbook.Setup(w => w.Worksheets).Returns(sheets.Object);            

            IDictionary<int, Excel.Worksheet> result = ExcelMocking.Class1.GetApplicableYearSheets(workbook.Object);
        }
    }
}