Mocking OpenXML with Moq

本文关键字:Moq with OpenXML Mocking | 更新日期: 2023-09-27 17:59:13

我应该如何测试以下GetWorksheetPart方法:

public class ExcelDocument : IExcelDocument
{
    private readonly string _filePath;
    public ExcelDocument(string filePath)
    {
        _filePath = filePath;
    }
    public WorksheetPart GetWorksheetPart(ISpreadsheetDocument excelDoc, string sheetName)
    {
        Sheet sheet = excelDoc.GetSheet(sheetName);
        if (sheet == null)
        {
            throw new ArgumentException(
                String.Format("No sheet named {0} found in spreadsheet {1}",
                    sheetName, _filePath), "sheetName");
        }
        return excelDoc.GetPartById(sheet.Id);
    }
}

其中IExcelDocument和包装器的SpreadsheetDocumentWrapper接口为:

public interface IExcelDocument
{
    WorksheetPart GetWorksheetPart(ISpreadsheetDocument excelDoc, string sheetName);
}
public interface ISpreadsheetDocument
{
    Sheet GetSheet(string name);
    WorksheetPart GetPartById(string id);
}

这是包装纸本身。

public class SpreadsheetDocumentWrapper : ISpreadsheetDocument
{
    private SpreadsheetDocument excelDoc;
    public SpreadsheetDocumentWrapper(SpreadsheetDocument excelDoc)
    {
        this.excelDoc = excelDoc;
    }
    public Sheet GetSheet(string sheetName)
    {
        return excelDoc.WorkbookPart.Workbook.Descendants<Sheet>()
                   .SingleOrDefault(s => s.Name == sheetName);
    }
    public WorksheetPart GetPartById(string id)
    {
        return (WorksheetPart)excelDoc.WorkbookPart.GetPartById(id);
    }
}

最后,对GetWorksheetPart进行了测试。问题是我不知道如何测试这个功能。我们的想法是在给定工作表名称和电子表格文档的情况下获得WorksheetPart

public class Test
{
    [TestClass()]
    public class ExcelUpdateLogicTests
    {
        [TestMethod()]
        public void Excel_GetWorkseetPartTest()
        {
            Mock<ISpreadsheetDocument> mockSpreadhseet = new Mock<ISpreadsheetDocument>();
            Sheet sheet = new Sheet();
            string id = "1";
            sheet.Name = "sheet";
            sheet.Id = id;
            mockSpreadhseet.Setup(doc => doc.GetSheet("sheet")).Returns(sheet);
            mockSpreadhseet.Setup(doc => doc.GetPartById(id)).Returns(????);
            Mock<IExcelDocument> mockExcelDocument = new Mock<IExcelDocument>();
            WorksheetPart mockWorkseet = mockExcelDocument.Object
                .GetWorksheetPart(mockSpreadhseet.Object, "sheet");
            Assert.IsTrue(mockWorkseet.GetIdOfPart(mockWorkseet) == id);
        }
    }
}

以下是电子表格的通用OpenXML结构树:

                Spreadsheet
                      |         
                 WorkbookPart    
           /         |             '
   Workbook WorkbookStylesPart WorksheetPart
         |          |               |
    Sheets     StyleSheet        Worksheet
        |                        /        '       
  (refers to               SheetData        Columns  
   Worksheetparts)            |   
                             Rows

Mocking OpenXML with Moq

由于ExcelDocument是测试中的系统,因此无需模拟IExcelDocument。您应该模拟/存根/伪造测试SUT 所需的依赖关系

现在我能让你的考试像这样通过。。。

public class Test {
    [TestClass()]
    public class ExcelUpdateLogicTests {
        [TestMethod()]
        public void Given_SheetName_ExcelDocument_Should_GetWorkseetPart() {
            //Arrange
            var stream = new MemoryStream();//Avoid having to use actual file on disk
            var spreadsheetDocument = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.Workbook);
            // Add a WorkbookPart.
            WorkbookPart workbookpart = spreadsheetDocument.AddWorkbookPart();
            workbookpart.Workbook = new Workbook();
            // Add a WorksheetPart.
            WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
            worksheetPart.Worksheet = new Worksheet(new SheetData());
            // Add a sheets list.
            Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets());
            // Append the new worksheet and associate it with the workbook.
            string expectedId = workbookpart.GetIdOfPart(worksheetPart);
            string sheetName = "mySheet";
            Sheet sheet = new Sheet() {
                Id = expectedId,
                SheetId = 1,
                Name = sheetName
            };
            sheets.Append(sheet);
            var wrapper = new SpreadsheetDocumentWrapper(spreadsheetDocument);
            string fakeFilePath = "path";
            var sut = new ExcelDocument(fakeFilePath);
            //Act
            WorksheetPart result = sut.GetWorksheetPart(wrapper, sheetName);
            //Assert
            Assert.IsNotNull(result);
            var actualId = workbookpart.GetIdOfPart(result);
            Assert.AreEqual(expectedId, actualId);
        }
    }
}

然而,这样做的过程对当前的设计提出了一些问题。

如果创建抽象的全部目的是隐藏实现细节,减少外部框架上的紧密耦合,使模拟和测试变得更容易,那么必须创建一个实际的SpreadsheetDocument并将其包装用于测试似乎是多余的。

考虑到内部生成,框架的许多部分很难模拟。我会把这些隐藏在其他抽象后面,但鉴于我对这个系统的最终目标了解不够,我无法建议你应该采用什么设计结构。