问题
Okay, to reproduce, here is what you need
public interface IWorkbookSet
{
IWorkbooks Workbooks { get; }
}
public interface IWorkbooks : IEnumerable
{
IWorkbook this[int index] { get; }
IWorkbook this[string name] { get; }
int Count { get; }
}
public interface IWorkbook
{
IWorksheets Worksheets { get; }
}
public interface IWorksheets : IEnumerable
{
IWorksheet this[int index] { get; }
IWorksheet this[string name] { get; }
int Count { get; }
IWorksheet Add();
IWorksheet AddAfter(IWorksheet sheet);
IWorksheet AddBefore(IWorksheet sheet);
bool Contains(IWorksheet worksheet);
}
public interface IWorksheet
{
string Name { get; set; }
}
Set up a Microsoft unit test using the following code
[TestInitialize]
public void Initialize()
{
List<string> fakeSheetNames = new List<string>()
{
"Master", "A", "B", "C", "__ParentA", "D", "wsgParentB", "E", "F", "__ParentC", "__ParentD", "G"
};
// Worksheets.
var fakeWorksheetsList = new List<IWorksheet>();
foreach (string name in fakeSheetNames)
{
var tmpMock = new Mock<IWorksheet>();
tmpMock.Setup(p => p.Name).Returns(name);
tmpMock.Setup(p => p.Visible)
.Returns(parentPrefixes.Any(p => name.StartsWith(p)) ?
SheetVisibility.Hidden :
SheetVisibility.Visible);
fakeWorksheetsList.Add(tmpMock.Object);
}
var mockWorksheets = new Mock<IWorksheets>();
mockWorksheets.Setup(m => m[It.IsAny<int>()]).Returns<int>(index => fakeWorksheetsList[index]);
mockWorksheets.Setup(m => m.GetEnumerator()).Returns(fakeWorksheetsList.GetEnumerator());
mockWorksheets.SetupGet(m => m.Count).Returns(fakeWorksheetsList.Count);
// Workbook.
var mockWorkbook = new Mock<IWorkbook>();
mockWorkbook.Setup(p => p.Name).Returns("Name");
mockWorkbook.Setup(p => p.FullName).Returns("FullName");
mockWorkbook.Setup(p => p.Worksheets).Returns(mockWorksheets.Object);
// Workbooks.
var fakeWorkbooksList = new List<IWorkbook>() { mockWorkbook.Object };
var mockWorkbooks = new Mock<IWorkbooks>();
mockWorkbooks.Setup(m => m[It.IsAny<int>()]).Returns<int>(index => fakeWorkbooksList[index]);
mockWorkbooks.Setup(m => m.GetEnumerator()).Returns(fakeWorkbooksList.GetEnumerator());
mockWorkbooks.SetupGet(m => m.Count).Returns(fakeWorkbooksList.Count);
// WorkbookSet.
mockWorkbookSet = new Mock<IWorkbookSet>();
mockWorkbookSet.Setup(m => m.Workbooks).Returns(mockWorkbooks.Object);
var expectedWorkBooksIndex = 0;
var expectedWorkSheetIndex = 1;
var expected = fakeWorksheetsList[expectedWorkSheetIndex];
// Setup test.
var workbookSet = mockWorkbookSet.Object;
var actual = workbookSet
.Workbooks[expectedWorkBooksIndex]
.Worksheets[expectedWorkSheetIndex];
Assert.AreEqual(expected, actual);
Assert.AreEqual(12, workbookSet.Workbooks[0].Worksheets.Count);
}
Now in a test method, do this
[TestMethod]
public async Task StrucutreGenerationAsyncTest()
{
foreach (IWorksheet ws in mockWorkbookSet.Object.Workbooks[0].Worksheets)
Trace.WriteLine("1111 ws = " + ws.Name);
foreach (IWorksheet ws in mockWorkbookSet.Object.Workbooks[0].Worksheets)
Trace.WriteLine("2222 ws = " + ws.Name);
}
Output:
Test Name: StrucutreGenerationAsyncTest
Test Outcome: Passed
Result StandardOutput:
Debug Trace:
1111 ws = Master
1111 ws = A
1111 ws = B
1111 ws = C
1111 ws = __ParentA
1111 ws = D
1111 ws = wsgParentB
1111 ws = E
1111 ws = F
1111 ws = __ParentC
1111 ws = __ParentD
1111 ws = G
The first foreach
enumerate the IWorksheets
, the second does not(?) as mockWorkbookSet.Object.Workbooks[0].Worksheets
is now empty.
Even more odd is this
[TestMethod]
public async Task StrucutreGenerationAsyncTest()
{
if (mockWorkbookSet.Object.Workbooks[0].Worksheets
.Cast<IWorksheet>().Any(ws => ws.Name.Compare("Master")))
Trace.WriteLine("Match!");
foreach (IWorksheet ws in mockWorkbookSet.Object.Workbooks[0].Worksheets)
Trace.WriteLine("1111 ws = " + ws.Name);
foreach (IWorksheet ws in mockWorkbookSet.Object.Workbooks[0].Worksheets)
Trace.WriteLine("2222 ws = " + ws.Name);
}
Output:
Test Name: StrucutreGenerationAsyncTest
Test Outcome: Passed
Result StandardOutput:
Debug Trace:
Match!
1111 ws = A
1111 ws = B
1111 ws = C
1111 ws = __ParentA
1111 ws = D
1111 ws = wsgParentB
1111 ws = E
1111 ws = F
1111 ws = __ParentC
1111 ws = __ParentD
1111 ws = G
Where has "Master" gone? It is like the act of enumerating removes items from the collection. Why is this happening and how can I fix it?
Edit #1: I have tried mocking the enumerator using a method, as follows
var mockWorksheets = new Mock<IWorksheets>();
mockWorksheets.Setup(m => m[It.IsAny<int>()]).Returns<int>(index => fakeWorksheetsList[index]);
mockWorksheets.Setup(m => m.GetEnumerator()).Returns(fakeWorksheetsList.GetEnumerator());
mockWorksheets.SetupGet(m => m.Count).Returns(fakeWorksheetsList.Count);
With
private IEnumerator<IWorksheet> WorksheetList()
{
foreach (string name in fakeSheetNames)
{
var mock = new Mock<IWorksheet>();
mock.Setup(p => p.Name).Returns(name);
mock.Setup(p => p.Visible)
.Returns(parentPrefixes.Any(p => name.StartsWith(p)) ?
SheetVisibility.Hidden :
SheetVisibility.Visible);
yield return mock.Object;
}
}
This does not help.
回答1:
mockWorksheets.Setup(m => m.GetEnumerator()).Returns(fakeWorksheetsList.GetEnumerator());
returns the same enumerator instance every time which when used once will need to be reset (resulting in the appearance of an empty collection on any subsequent enumeration).
If you want a new enumerator on every call, then you need to pass Returns
a lambda expression:
mockWorkSheets.Setup(m => m.GetEnumerator()).Returns(() => fakeWorksheetsList.GetEnumerator());
The lambda will get called every time GetEnumerator()
is called. So now enumerating the mock multiple times should then work as expected.
Reference Moq First() Last() and GetEnumerator() wierdness
来源:https://stackoverflow.com/questions/39147032/enumerating-a-mocked-indexer-property-causes-the-collection-to-become-empty