6952f686fa
- Add WorkbookFixtureBase and 4 concrete fixtures for shared workbooks - Add ExcelTestHelpers with shared utility methods - Create Integration/ folder with 7 fixture-based test classes: - MinimalSearchTests (5 tests) - SearchResultsSheetTests (5 tests) - MisInfoSheetTests (11 tests) - InvestigationSheetTests (7 tests) - ProtectionAndStyleTests (7 tests) - LegacyFormatTests (5 tests) - LargeDataSetTests (1 test) - Delete redundant ExcelExportIntegrationTests.cs (26 tests) - Delete redundant LegacyComparisonTests.cs (16 tests) - Reduce workbook generations from ~42 to 4 fixtures - Test runtime reduced from ~18 min to ~4 min (76% improvement) - All 122 ExcelIO tests pass
1031 lines
31 KiB
Markdown
1031 lines
31 KiB
Markdown
# ExcelIO Test Optimization Implementation Plan
|
|
|
|
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
|
|
**Goal:** Reduce ExcelIO.Tests runtime from ~18 minutes to ~3-4 minutes by using fixture-based test consolidation.
|
|
|
|
**Architecture:** Replace per-test workbook generation with xUnit `IClassFixture<T>` pattern. Generate 4 shared workbooks once, reuse across 42 integration tests.
|
|
|
|
**Tech Stack:** xUnit fixtures, ClosedXML, existing ExcelExportService
|
|
|
|
---
|
|
|
|
## Task 1: Create Fixture Base Class and Infrastructure
|
|
|
|
**Files:**
|
|
- Create: `tests/JdeScoping.ExcelIO.Tests/Fixtures/WorkbookFixtureBase.cs`
|
|
- Create: `tests/JdeScoping.ExcelIO.Tests/Fixtures/ExcelTestHelpers.cs`
|
|
|
|
**Step 1: Create Fixtures directory**
|
|
|
|
```bash
|
|
mkdir -p tests/JdeScoping.ExcelIO.Tests/Fixtures
|
|
```
|
|
|
|
**Step 2: Write WorkbookFixtureBase.cs**
|
|
|
|
```csharp
|
|
using ClosedXML.Excel;
|
|
using JdeScoping.ExcelIO.Generators;
|
|
using JdeScoping.ExcelIO.Mapping;
|
|
using JdeScoping.ExcelIO.Mapping.Maps;
|
|
using JdeScoping.ExcelIO.Models.Reporting;
|
|
using JdeScoping.ExcelIO.Options;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using NSubstitute;
|
|
|
|
namespace JdeScoping.ExcelIO.Tests.Fixtures;
|
|
|
|
public abstract class WorkbookFixtureBase : IDisposable
|
|
{
|
|
public XLWorkbook Workbook { get; }
|
|
public SearchModel SearchModel { get; }
|
|
|
|
protected abstract SearchModel CreateSearchModel();
|
|
|
|
protected WorkbookFixtureBase()
|
|
{
|
|
SearchModel = CreateSearchModel();
|
|
var service = CreateExportService();
|
|
var bytes = service.GenerateAsync(SearchModel).GetAwaiter().GetResult();
|
|
Workbook = new XLWorkbook(new MemoryStream(bytes));
|
|
}
|
|
|
|
private static ExcelExportService CreateExportService()
|
|
{
|
|
var logger = Substitute.For<ILogger<ExcelExportService>>();
|
|
var options = Options.Create(new ExcelExportOptions
|
|
{
|
|
CriteriaSheetPassword = "TestCriteriaPass",
|
|
DataSheetPassword = "TestDataPass"
|
|
});
|
|
|
|
var registry = CreateTestRegistry();
|
|
var tableWriter = new FluentTableWriter(registry);
|
|
var criteriaGenerator = new CriteriaSheetGenerator(options, tableWriter);
|
|
|
|
return new ExcelExportService(logger, options, criteriaGenerator, tableWriter, registry);
|
|
}
|
|
|
|
private static ExcelMapRegistry CreateTestRegistry()
|
|
{
|
|
var registry = new ExcelMapRegistry();
|
|
|
|
registry.Register(new SearchResultMap());
|
|
registry.Register(new MisSearchResultMap());
|
|
registry.Register(new MisNonMatchSearchResultMap());
|
|
registry.Register(new TimespanFilterMap());
|
|
registry.Register(new WorkOrderFilterEntryMap());
|
|
registry.Register(new ItemNumberFilterEntryMap());
|
|
registry.Register(new ProfitCenterFilterEntryMap());
|
|
registry.Register(new WorkCenterFilterEntryMap());
|
|
registry.Register(new OperatorFilterEntryMap());
|
|
registry.Register(new ComponentLotFilterEntryMap());
|
|
registry.Register(new ItemOperationMisFilterEntryMap());
|
|
|
|
return registry;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Workbook.Dispose();
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 3: Write ExcelTestHelpers.cs**
|
|
|
|
```csharp
|
|
using ClosedXML.Excel;
|
|
|
|
namespace JdeScoping.ExcelIO.Tests.Fixtures;
|
|
|
|
public static class ExcelTestHelpers
|
|
{
|
|
public static List<string> GetHeadersFromSheet(IXLWorksheet sheet)
|
|
{
|
|
var headers = new List<string>();
|
|
var col = 1;
|
|
while (!sheet.Cell(1, col).IsEmpty())
|
|
{
|
|
headers.Add(sheet.Cell(1, col).Value.GetText());
|
|
col++;
|
|
}
|
|
return headers;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 4: Build and verify**
|
|
|
|
```bash
|
|
dotnet build tests/JdeScoping.ExcelIO.Tests
|
|
```
|
|
|
|
---
|
|
|
|
## Task 2: Create Concrete Fixture Classes
|
|
|
|
**Files:**
|
|
- Create: `tests/JdeScoping.ExcelIO.Tests/Fixtures/MinimalSearchFixture.cs`
|
|
- Create: `tests/JdeScoping.ExcelIO.Tests/Fixtures/WithResultsFixture.cs`
|
|
- Create: `tests/JdeScoping.ExcelIO.Tests/Fixtures/WithMisDataFixture.cs`
|
|
- Create: `tests/JdeScoping.ExcelIO.Tests/Fixtures/LargeDataSetFixture.cs`
|
|
|
|
**Step 1: Write MinimalSearchFixture.cs**
|
|
|
|
```csharp
|
|
using JdeScoping.ExcelIO.Models.Reporting;
|
|
|
|
namespace JdeScoping.ExcelIO.Tests.Fixtures;
|
|
|
|
public class MinimalSearchFixture : WorkbookFixtureBase
|
|
{
|
|
protected override SearchModel CreateSearchModel() => new()
|
|
{
|
|
Id = 1,
|
|
Name = "Minimal Search Test",
|
|
UserName = "testuser",
|
|
SubmitDt = new DateTime(2024, 1, 15, 14, 30, 45),
|
|
StartDt = new DateTime(2024, 1, 15, 14, 31, 0),
|
|
EndDt = new DateTime(2024, 1, 15, 14, 35, 0),
|
|
ExtractMisData = false,
|
|
Results = []
|
|
};
|
|
}
|
|
```
|
|
|
|
**Step 2: Write WithResultsFixture.cs**
|
|
|
|
```csharp
|
|
using JdeScoping.ExcelIO.Models.Reporting;
|
|
using SearchResult = JdeScoping.Core.Models.SearchResults.SearchResult;
|
|
|
|
namespace JdeScoping.ExcelIO.Tests.Fixtures;
|
|
|
|
public class WithResultsFixture : WorkbookFixtureBase
|
|
{
|
|
protected override SearchModel CreateSearchModel() => new()
|
|
{
|
|
Id = 1,
|
|
Name = "Search With Results",
|
|
UserName = "testuser",
|
|
SubmitDt = new DateTime(2024, 1, 15, 14, 30, 45),
|
|
StartDt = new DateTime(2024, 1, 15, 14, 31, 0),
|
|
EndDt = new DateTime(2024, 1, 15, 14, 35, 0),
|
|
ExtractMisData = false,
|
|
Results =
|
|
[
|
|
new SearchResult
|
|
{
|
|
WorkOrderNumber = 12345,
|
|
WorkOrderBranchCode = "001",
|
|
LotNumber = "LOT-001",
|
|
ItemNumber = "ITEM-001",
|
|
PlanningFamily = "PF01",
|
|
StockingType = "M",
|
|
OrderQuantity = 100,
|
|
HeldQuantity = 0,
|
|
ScrappedQuantity = 0,
|
|
ShippedQuantity = 50,
|
|
StepBranchCode = "001",
|
|
StepNumber = 10,
|
|
StepDescription = "Assembly",
|
|
FunctionOperationDescription = "Main assembly operation",
|
|
StepUpdateDt = new DateTime(2024, 1, 14, 10, 0, 0),
|
|
StatusCode = "50",
|
|
StatusDescription = "In Progress",
|
|
StatusUpdateDt = new DateTime(2024, 1, 14, 10, 0, 0),
|
|
Flagged = true
|
|
}
|
|
]
|
|
};
|
|
}
|
|
```
|
|
|
|
**Step 3: Write WithMisDataFixture.cs**
|
|
|
|
```csharp
|
|
using JdeScoping.ExcelIO.Models.Reporting;
|
|
using SearchResult = JdeScoping.Core.Models.SearchResults.SearchResult;
|
|
using MisSearchResult = JdeScoping.Core.Models.SearchResults.MisSearchResult;
|
|
using MisNonMatchSearchResult = JdeScoping.Core.Models.SearchResults.MisNonMatchSearchResult;
|
|
|
|
namespace JdeScoping.ExcelIO.Tests.Fixtures;
|
|
|
|
public class WithMisDataFixture : WorkbookFixtureBase
|
|
{
|
|
protected override SearchModel CreateSearchModel() => new()
|
|
{
|
|
Id = 1,
|
|
Name = "Search With MIS Data",
|
|
UserName = "testuser",
|
|
SubmitDt = new DateTime(2024, 1, 15, 14, 30, 45),
|
|
StartDt = new DateTime(2024, 1, 15, 14, 31, 0),
|
|
EndDt = new DateTime(2024, 1, 15, 14, 35, 0),
|
|
ExtractMisData = true,
|
|
Results =
|
|
[
|
|
new SearchResult
|
|
{
|
|
WorkOrderNumber = 12345,
|
|
WorkOrderBranchCode = "001",
|
|
LotNumber = "LOT-001",
|
|
ItemNumber = "ITEM-001",
|
|
PlanningFamily = "PF01",
|
|
StockingType = "M",
|
|
OrderQuantity = 100,
|
|
HeldQuantity = 0,
|
|
ScrappedQuantity = 0,
|
|
ShippedQuantity = 50,
|
|
StepBranchCode = "001",
|
|
StepNumber = 10,
|
|
StepDescription = "Assembly",
|
|
FunctionOperationDescription = "Main assembly",
|
|
StepUpdateDt = new DateTime(2024, 1, 14, 10, 0, 0),
|
|
StatusCode = "50",
|
|
StatusDescription = "In Progress",
|
|
Flagged = true
|
|
}
|
|
],
|
|
MisResults =
|
|
[
|
|
new MisSearchResult
|
|
{
|
|
ItemNumber = "ITEM-001",
|
|
SequenceNumber = "010",
|
|
MisNumber = "MIS-001",
|
|
RevId = "A",
|
|
ItemDescription = "Test Item",
|
|
Status = "Released",
|
|
ReleaseDate = new DateTime(2023, 12, 15),
|
|
BranchCode = "001",
|
|
JobStepSequenceNumber = 10,
|
|
MatchedSequenceNumber = 10,
|
|
RoutingMatch = true,
|
|
MasterMatch = true,
|
|
FunctionOperationDescription = "Assembly operation",
|
|
CharNumber = "001",
|
|
TestDescription = "Sample test description",
|
|
SamplingType = "100%",
|
|
SamplingValue = "1",
|
|
ToolsGauges = "Gauge A, Gauge B",
|
|
WorkInstructions = "Step 1: Do this. Step 2: Do that."
|
|
}
|
|
],
|
|
MisNonMatchResults =
|
|
[
|
|
new MisNonMatchSearchResult
|
|
{
|
|
WorkCenterCode = "WC01",
|
|
WorkOrderNumber = 12345,
|
|
WorkOrderStartDate = new DateTime(2024, 1, 8),
|
|
JobStepNumber = 10,
|
|
JobStepDescription = "Test operation",
|
|
JobStepEndDate = new DateTime(2024, 1, 10),
|
|
FunctionCode = "FC01",
|
|
WasJobStepAdded = false,
|
|
MatchedJobStepNumber = 10,
|
|
ItemNumber = "ITEM-001",
|
|
ItemDescription = "Test Item Description",
|
|
RoutingType = "M"
|
|
}
|
|
]
|
|
};
|
|
}
|
|
```
|
|
|
|
**Step 4: Write LargeDataSetFixture.cs**
|
|
|
|
```csharp
|
|
using JdeScoping.ExcelIO.Models.Reporting;
|
|
using SearchResult = JdeScoping.Core.Models.SearchResults.SearchResult;
|
|
|
|
namespace JdeScoping.ExcelIO.Tests.Fixtures;
|
|
|
|
public class LargeDataSetFixture : WorkbookFixtureBase
|
|
{
|
|
protected override SearchModel CreateSearchModel()
|
|
{
|
|
var model = new SearchModel
|
|
{
|
|
Id = 1,
|
|
Name = "Large Data Set Test",
|
|
UserName = "testuser",
|
|
SubmitDt = new DateTime(2024, 1, 15, 14, 30, 45),
|
|
StartDt = new DateTime(2024, 1, 15, 14, 31, 0),
|
|
EndDt = new DateTime(2024, 1, 15, 14, 35, 0),
|
|
ExtractMisData = false,
|
|
Results = []
|
|
};
|
|
|
|
for (int i = 0; i < 1000; i++)
|
|
{
|
|
model.Results.Add(new SearchResult
|
|
{
|
|
WorkOrderNumber = 10000 + i,
|
|
ItemNumber = $"ITEM-{i:D5}",
|
|
LotNumber = $"LOT-{i:D5}",
|
|
Flagged = true
|
|
});
|
|
}
|
|
|
|
return model;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 5: Build and verify**
|
|
|
|
```bash
|
|
dotnet build tests/JdeScoping.ExcelIO.Tests
|
|
```
|
|
|
|
---
|
|
|
|
## Task 3: Create MinimalSearchTests
|
|
|
|
**Files:**
|
|
- Create: `tests/JdeScoping.ExcelIO.Tests/Integration/MinimalSearchTests.cs`
|
|
|
|
**Step 1: Create Integration directory**
|
|
|
|
```bash
|
|
mkdir -p tests/JdeScoping.ExcelIO.Tests/Integration
|
|
```
|
|
|
|
**Step 2: Write MinimalSearchTests.cs**
|
|
|
|
```csharp
|
|
using ClosedXML.Excel;
|
|
using JdeScoping.ExcelIO.Tests.Fixtures;
|
|
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace JdeScoping.ExcelIO.Tests.Integration;
|
|
|
|
public class MinimalSearchTests : IClassFixture<MinimalSearchFixture>
|
|
{
|
|
private readonly XLWorkbook _workbook;
|
|
|
|
public MinimalSearchTests(MinimalSearchFixture fixture)
|
|
{
|
|
_workbook = fixture.Workbook;
|
|
}
|
|
|
|
[Fact]
|
|
public void SheetCount_IsTwo()
|
|
{
|
|
_workbook.Worksheets.Count.ShouldBe(2);
|
|
}
|
|
|
|
[Fact]
|
|
public void SearchCriteriaSheet_Exists()
|
|
{
|
|
_workbook.Worksheets.TryGetWorksheet("Search Criteria", out _).ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void SearchResultsSheet_Exists()
|
|
{
|
|
_workbook.Worksheets.TryGetWorksheet("Search Results", out _).ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void SearchCriteriaSheet_IsProtected()
|
|
{
|
|
var sheet = _workbook.Worksheet("Search Criteria");
|
|
sheet.Protection.IsProtected.ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void SearchResultsSheet_IsProtected()
|
|
{
|
|
var sheet = _workbook.Worksheet("Search Results");
|
|
sheet.Protection.IsProtected.ShouldBeTrue();
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 3: Build and run tests**
|
|
|
|
```bash
|
|
dotnet build tests/JdeScoping.ExcelIO.Tests
|
|
dotnet test tests/JdeScoping.ExcelIO.Tests --filter "FullyQualifiedName~MinimalSearchTests" --verbosity normal
|
|
```
|
|
|
|
---
|
|
|
|
## Task 4: Create SearchResultsSheetTests
|
|
|
|
**Files:**
|
|
- Create: `tests/JdeScoping.ExcelIO.Tests/Integration/SearchResultsSheetTests.cs`
|
|
|
|
**Step 1: Write SearchResultsSheetTests.cs**
|
|
|
|
```csharp
|
|
using ClosedXML.Excel;
|
|
using JdeScoping.ExcelIO.Tests.Fixtures;
|
|
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace JdeScoping.ExcelIO.Tests.Integration;
|
|
|
|
public class SearchResultsSheetTests : IClassFixture<WithResultsFixture>
|
|
{
|
|
private readonly XLWorkbook _workbook;
|
|
private readonly IXLWorksheet _sheet;
|
|
private readonly List<string> _headers;
|
|
|
|
public SearchResultsSheetTests(WithResultsFixture fixture)
|
|
{
|
|
_workbook = fixture.Workbook;
|
|
_sheet = _workbook.Worksheet("Search Results");
|
|
_headers = ExcelTestHelpers.GetHeadersFromSheet(_sheet);
|
|
}
|
|
|
|
[Fact]
|
|
public void ColumnCount_Is19()
|
|
{
|
|
_headers.Count.ShouldBe(19);
|
|
}
|
|
|
|
[Fact]
|
|
public void ColumnHeaders_MatchExpected()
|
|
{
|
|
_headers.ShouldContain("Work Order Number");
|
|
_headers.ShouldContain("Work Order Branch Code");
|
|
_headers.ShouldContain("Lot Number");
|
|
_headers.ShouldContain("Item Number");
|
|
_headers.ShouldContain("Planning Family");
|
|
_headers.ShouldContain("Stocking Type");
|
|
_headers.ShouldContain("Order Quantity");
|
|
_headers.ShouldContain("Held Quantity");
|
|
_headers.ShouldContain("Scrapped Quantity");
|
|
_headers.ShouldContain("Shipped Quantity");
|
|
_headers.ShouldContain("Operation Step Branch Code");
|
|
_headers.ShouldContain("Operation Step");
|
|
_headers.ShouldContain("Operation Step Description");
|
|
_headers.ShouldContain("Function Operation Description");
|
|
_headers.ShouldContain("Operation Step Update Timestamp");
|
|
_headers.ShouldContain("Status Code");
|
|
_headers.ShouldContain("Status Description");
|
|
_headers.ShouldContain("Status Update Timestamp");
|
|
_headers.ShouldContain("Inclusion Reason");
|
|
}
|
|
|
|
[Fact]
|
|
public void ColumnOrder_MatchesSpec()
|
|
{
|
|
_headers[0].ShouldBe("Work Order Number");
|
|
_headers[1].ShouldBe("Work Order Branch Code");
|
|
_headers[2].ShouldBe("Lot Number");
|
|
_headers[3].ShouldBe("Item Number");
|
|
_headers[4].ShouldBe("Planning Family");
|
|
_headers[5].ShouldBe("Stocking Type");
|
|
_headers[6].ShouldBe("Order Quantity");
|
|
_headers[7].ShouldBe("Held Quantity");
|
|
_headers[8].ShouldBe("Scrapped Quantity");
|
|
_headers[9].ShouldBe("Shipped Quantity");
|
|
_headers[10].ShouldBe("Operation Step Branch Code");
|
|
_headers[11].ShouldBe("Operation Step");
|
|
_headers[12].ShouldBe("Operation Step Description");
|
|
_headers[13].ShouldBe("Function Operation Description");
|
|
_headers[14].ShouldBe("Operation Step Update Timestamp");
|
|
_headers[15].ShouldBe("Status Code");
|
|
_headers[16].ShouldBe("Status Description");
|
|
_headers[17].ShouldBe("Status Update Timestamp");
|
|
_headers[18].ShouldBe("Inclusion Reason");
|
|
}
|
|
|
|
[Fact]
|
|
public void TableStyle_IsLight18()
|
|
{
|
|
var table = _sheet.Tables.First();
|
|
table.Theme.ShouldBe(XLTableTheme.TableStyleLight18);
|
|
}
|
|
|
|
[Fact]
|
|
public void DataRow_ContainsExpectedValues()
|
|
{
|
|
_sheet.Cell(2, 1).Value.GetNumber().ShouldBe(12345);
|
|
_sheet.Cell(2, 3).Value.GetText().ShouldBe("LOT-001");
|
|
_sheet.Cell(2, 4).Value.GetText().ShouldBe("ITEM-001");
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 2: Build and run tests**
|
|
|
|
```bash
|
|
dotnet build tests/JdeScoping.ExcelIO.Tests
|
|
dotnet test tests/JdeScoping.ExcelIO.Tests --filter "FullyQualifiedName~SearchResultsSheetTests" --verbosity normal
|
|
```
|
|
|
|
---
|
|
|
|
## Task 5: Create MisInfoSheetTests
|
|
|
|
**Files:**
|
|
- Create: `tests/JdeScoping.ExcelIO.Tests/Integration/MisInfoSheetTests.cs`
|
|
|
|
**Step 1: Write MisInfoSheetTests.cs**
|
|
|
|
```csharp
|
|
using ClosedXML.Excel;
|
|
using JdeScoping.ExcelIO.Tests.Fixtures;
|
|
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace JdeScoping.ExcelIO.Tests.Integration;
|
|
|
|
public class MisInfoSheetTests : IClassFixture<WithMisDataFixture>
|
|
{
|
|
private readonly XLWorkbook _workbook;
|
|
private readonly IXLWorksheet _sheet;
|
|
private readonly List<string> _headers;
|
|
|
|
public MisInfoSheetTests(WithMisDataFixture fixture)
|
|
{
|
|
_workbook = fixture.Workbook;
|
|
_sheet = _workbook.Worksheet("MIS Info");
|
|
_headers = ExcelTestHelpers.GetHeadersFromSheet(_sheet);
|
|
}
|
|
|
|
[Fact]
|
|
public void SheetCount_IsFour()
|
|
{
|
|
_workbook.Worksheets.Count.ShouldBe(4);
|
|
}
|
|
|
|
[Fact]
|
|
public void MisInfoSheet_Exists()
|
|
{
|
|
_workbook.Worksheets.TryGetWorksheet("MIS Info", out _).ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void ColumnCount_Is19()
|
|
{
|
|
_headers.Count.ShouldBe(19);
|
|
}
|
|
|
|
[Fact]
|
|
public void ColumnHeaders_MatchExpected()
|
|
{
|
|
_headers.ShouldContain("Item Number");
|
|
_headers.ShouldContain("MIS Job Step Sequence Number");
|
|
_headers.ShouldContain("MIS Number");
|
|
_headers.ShouldContain("MIS Revision");
|
|
_headers.ShouldContain("Item Description");
|
|
_headers.ShouldContain("MIS Release Status");
|
|
_headers.ShouldContain("MIS Release Date");
|
|
_headers.ShouldContain("Branch Code");
|
|
_headers.ShouldContain("Job Step Sequence Number");
|
|
_headers.ShouldContain("Matched Sequence Number");
|
|
_headers.ShouldContain("Matched to F3112Z1?");
|
|
_headers.ShouldContain("Matched to F3003?");
|
|
_headers.ShouldContain("Function Operation Description");
|
|
_headers.ShouldContain("Char Number");
|
|
_headers.ShouldContain("Test Description");
|
|
_headers.ShouldContain("Sampling Type");
|
|
_headers.ShouldContain("Sampling Value");
|
|
_headers.ShouldContain("Tools & Gauges");
|
|
_headers.ShouldContain("Work Instructions");
|
|
}
|
|
|
|
[Fact]
|
|
public void ColumnOrder_MatchesSpec()
|
|
{
|
|
_headers[0].ShouldBe("Item Number");
|
|
_headers[1].ShouldBe("MIS Job Step Sequence Number");
|
|
_headers[2].ShouldBe("MIS Number");
|
|
_headers[3].ShouldBe("MIS Revision");
|
|
_headers[4].ShouldBe("Item Description");
|
|
_headers[5].ShouldBe("MIS Release Status");
|
|
_headers[6].ShouldBe("MIS Release Date");
|
|
_headers[7].ShouldBe("Branch Code");
|
|
_headers[8].ShouldBe("Job Step Sequence Number");
|
|
_headers[9].ShouldBe("Matched Sequence Number");
|
|
_headers[10].ShouldBe("Matched to F3112Z1?");
|
|
_headers[11].ShouldBe("Matched to F3003?");
|
|
_headers[12].ShouldBe("Function Operation Description");
|
|
_headers[13].ShouldBe("Char Number");
|
|
_headers[14].ShouldBe("Test Description");
|
|
_headers[15].ShouldBe("Sampling Type");
|
|
_headers[16].ShouldBe("Sampling Value");
|
|
_headers[17].ShouldBe("Tools & Gauges");
|
|
_headers[18].ShouldBe("Work Instructions");
|
|
}
|
|
|
|
[Fact]
|
|
public void TableStyle_IsLight18()
|
|
{
|
|
var table = _sheet.Tables.First();
|
|
table.Theme.ShouldBe(XLTableTheme.TableStyleLight18);
|
|
}
|
|
|
|
[Fact]
|
|
public void Protection_IsEnabled()
|
|
{
|
|
_sheet.Protection.IsProtected.ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void DataRow_ContainsExpectedValues()
|
|
{
|
|
_sheet.Cell(2, 1).Value.GetText().ShouldBe("ITEM-001");
|
|
_sheet.Cell(2, 3).Value.GetText().ShouldBe("MIS-001");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestDescriptionColumn_IsWrapped()
|
|
{
|
|
var colIndex = _headers.IndexOf("Test Description") + 1;
|
|
_sheet.Column(colIndex).Width.ShouldBe(65);
|
|
_sheet.Column(colIndex).Style.Alignment.WrapText.ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void ToolsGaugesColumn_IsWrapped()
|
|
{
|
|
var colIndex = _headers.IndexOf("Tools & Gauges") + 1;
|
|
_sheet.Column(colIndex).Width.ShouldBe(65);
|
|
_sheet.Column(colIndex).Style.Alignment.WrapText.ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void WorkInstructionsColumn_IsWrapped()
|
|
{
|
|
var colIndex = _headers.IndexOf("Work Instructions") + 1;
|
|
_sheet.Column(colIndex).Width.ShouldBe(65);
|
|
_sheet.Column(colIndex).Style.Alignment.WrapText.ShouldBeTrue();
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 2: Build and run tests**
|
|
|
|
```bash
|
|
dotnet build tests/JdeScoping.ExcelIO.Tests
|
|
dotnet test tests/JdeScoping.ExcelIO.Tests --filter "FullyQualifiedName~MisInfoSheetTests" --verbosity normal
|
|
```
|
|
|
|
---
|
|
|
|
## Task 6: Create InvestigationSheetTests
|
|
|
|
**Files:**
|
|
- Create: `tests/JdeScoping.ExcelIO.Tests/Integration/InvestigationSheetTests.cs`
|
|
|
|
**Step 1: Write InvestigationSheetTests.cs**
|
|
|
|
```csharp
|
|
using ClosedXML.Excel;
|
|
using JdeScoping.ExcelIO.Tests.Fixtures;
|
|
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace JdeScoping.ExcelIO.Tests.Integration;
|
|
|
|
public class InvestigationSheetTests : IClassFixture<WithMisDataFixture>
|
|
{
|
|
private readonly XLWorkbook _workbook;
|
|
private readonly IXLWorksheet _sheet;
|
|
private readonly List<string> _headers;
|
|
|
|
public InvestigationSheetTests(WithMisDataFixture fixture)
|
|
{
|
|
_workbook = fixture.Workbook;
|
|
_sheet = _workbook.Worksheet("Investigation");
|
|
_headers = ExcelTestHelpers.GetHeadersFromSheet(_sheet);
|
|
}
|
|
|
|
[Fact]
|
|
public void InvestigationSheet_Exists()
|
|
{
|
|
_workbook.Worksheets.TryGetWorksheet("Investigation", out _).ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void ColumnCount_Is12()
|
|
{
|
|
_headers.Count.ShouldBe(12);
|
|
}
|
|
|
|
[Fact]
|
|
public void ColumnHeaders_MatchExpected()
|
|
{
|
|
_headers.ShouldContain("Work Center Code");
|
|
_headers.ShouldContain("Work Order Number");
|
|
_headers.ShouldContain("Work Order Start Date");
|
|
_headers.ShouldContain("Job Step Number");
|
|
_headers.ShouldContain("Function Operation Description");
|
|
_headers.ShouldContain("Job Step End Date");
|
|
_headers.ShouldContain("Function Code");
|
|
_headers.ShouldContain("Was Job Step Added?");
|
|
_headers.ShouldContain("Matched Job Step Number");
|
|
_headers.ShouldContain("Item Number");
|
|
_headers.ShouldContain("Item Description");
|
|
_headers.ShouldContain("Routing Type");
|
|
}
|
|
|
|
[Fact]
|
|
public void ColumnOrder_MatchesSpec()
|
|
{
|
|
_headers[0].ShouldBe("Work Center Code");
|
|
_headers[1].ShouldBe("Work Order Number");
|
|
_headers[2].ShouldBe("Work Order Start Date");
|
|
_headers[3].ShouldBe("Job Step Number");
|
|
_headers[4].ShouldBe("Function Operation Description");
|
|
_headers[5].ShouldBe("Job Step End Date");
|
|
_headers[6].ShouldBe("Function Code");
|
|
_headers[7].ShouldBe("Was Job Step Added?");
|
|
_headers[8].ShouldBe("Matched Job Step Number");
|
|
_headers[9].ShouldBe("Item Number");
|
|
_headers[10].ShouldBe("Item Description");
|
|
_headers[11].ShouldBe("Routing Type");
|
|
}
|
|
|
|
[Fact]
|
|
public void TableStyle_IsLight18()
|
|
{
|
|
var table = _sheet.Tables.First();
|
|
table.Theme.ShouldBe(XLTableTheme.TableStyleLight18);
|
|
}
|
|
|
|
[Fact]
|
|
public void Protection_IsEnabled()
|
|
{
|
|
_sheet.Protection.IsProtected.ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void DataRow_ContainsExpectedValues()
|
|
{
|
|
_sheet.Cell(2, 2).Value.GetNumber().ShouldBe(12345);
|
|
_sheet.Cell(2, 10).Value.GetText().ShouldBe("ITEM-001");
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 2: Build and run tests**
|
|
|
|
```bash
|
|
dotnet build tests/JdeScoping.ExcelIO.Tests
|
|
dotnet test tests/JdeScoping.ExcelIO.Tests --filter "FullyQualifiedName~InvestigationSheetTests" --verbosity normal
|
|
```
|
|
|
|
---
|
|
|
|
## Task 7: Create ProtectionAndStyleTests
|
|
|
|
**Files:**
|
|
- Create: `tests/JdeScoping.ExcelIO.Tests/Integration/ProtectionAndStyleTests.cs`
|
|
|
|
**Step 1: Write ProtectionAndStyleTests.cs**
|
|
|
|
```csharp
|
|
using ClosedXML.Excel;
|
|
using JdeScoping.ExcelIO.Tests.Fixtures;
|
|
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace JdeScoping.ExcelIO.Tests.Integration;
|
|
|
|
public class ProtectionAndStyleTests : IClassFixture<WithMisDataFixture>
|
|
{
|
|
private readonly XLWorkbook _workbook;
|
|
|
|
public ProtectionAndStyleTests(WithMisDataFixture fixture)
|
|
{
|
|
_workbook = fixture.Workbook;
|
|
}
|
|
|
|
[Fact]
|
|
public void AllDataSheets_AreProtected()
|
|
{
|
|
_workbook.Worksheet("Search Results").Protection.IsProtected.ShouldBeTrue();
|
|
_workbook.Worksheet("MIS Info").Protection.IsProtected.ShouldBeTrue();
|
|
_workbook.Worksheet("Investigation").Protection.IsProtected.ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void Protection_AllowsFiltering()
|
|
{
|
|
var sheet = _workbook.Worksheet("Search Results");
|
|
sheet.Protection.AllowedElements.HasFlag(XLSheetProtectionElements.AutoFilter).ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void Protection_AllowsSorting()
|
|
{
|
|
var sheet = _workbook.Worksheet("Search Results");
|
|
sheet.Protection.AllowedElements.HasFlag(XLSheetProtectionElements.Sort).ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void Protection_AllowsFormatting()
|
|
{
|
|
var sheet = _workbook.Worksheet("Search Results");
|
|
sheet.Protection.AllowedElements.HasFlag(XLSheetProtectionElements.FormatCells).ShouldBeTrue();
|
|
sheet.Protection.AllowedElements.HasFlag(XLSheetProtectionElements.FormatColumns).ShouldBeTrue();
|
|
sheet.Protection.AllowedElements.HasFlag(XLSheetProtectionElements.FormatRows).ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void AllTables_UseLight18Style()
|
|
{
|
|
_workbook.Worksheet("Search Results").Tables.First().Theme.ShouldBe(XLTableTheme.TableStyleLight18);
|
|
_workbook.Worksheet("MIS Info").Tables.First().Theme.ShouldBe(XLTableTheme.TableStyleLight18);
|
|
_workbook.Worksheet("Investigation").Tables.First().Theme.ShouldBe(XLTableTheme.TableStyleLight18);
|
|
}
|
|
|
|
[Fact]
|
|
public void HeaderCells_HaveCorrectFormatting()
|
|
{
|
|
var sheet = _workbook.Worksheet("Search Criteria");
|
|
var headerCell = sheet.Cell(1, 1);
|
|
headerCell.Style.Font.Bold.ShouldBeTrue();
|
|
headerCell.Style.Fill.BackgroundColor.ShouldBe(XLColor.Gainsboro);
|
|
headerCell.Style.Alignment.Horizontal.ShouldBe(XLAlignmentHorizontalValues.Center);
|
|
}
|
|
|
|
[Fact]
|
|
public void CriteriaTimestamp_MatchesLegacyFormat()
|
|
{
|
|
var sheet = _workbook.Worksheet("Search Criteria");
|
|
var timestamp = sheet.Cell(4, 2).Value.GetText();
|
|
timestamp.ShouldContain("Jan 15, 2024");
|
|
timestamp.ShouldContain("02:30:45");
|
|
timestamp.ShouldContain("EST");
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 2: Build and run tests**
|
|
|
|
```bash
|
|
dotnet build tests/JdeScoping.ExcelIO.Tests
|
|
dotnet test tests/JdeScoping.ExcelIO.Tests --filter "FullyQualifiedName~ProtectionAndStyleTests" --verbosity normal
|
|
```
|
|
|
|
---
|
|
|
|
## Task 8: Create LegacyFormatTests and LargeDataSetTests
|
|
|
|
**Files:**
|
|
- Create: `tests/JdeScoping.ExcelIO.Tests/Integration/LegacyFormatTests.cs`
|
|
- Create: `tests/JdeScoping.ExcelIO.Tests/Integration/LargeDataSetTests.cs`
|
|
|
|
**Step 1: Write LegacyFormatTests.cs**
|
|
|
|
```csharp
|
|
using Shouldly;
|
|
using Xunit;
|
|
using ExcelFormats = JdeScoping.ExcelIO.Formatting.ExcelFormats;
|
|
|
|
namespace JdeScoping.ExcelIO.Tests.Integration;
|
|
|
|
/// <summary>
|
|
/// Tests for ExcelFormats constants - no workbook generation needed.
|
|
/// </summary>
|
|
public class LegacyFormatTests
|
|
{
|
|
[Fact]
|
|
public void TimestampFormat_MatchesLegacy()
|
|
{
|
|
ExcelFormats.TimestampFormat.ShouldBe("[$-409]m/d/yy h:mm AM/PM;@");
|
|
}
|
|
|
|
[Fact]
|
|
public void DateFormat_MatchesLegacyPattern()
|
|
{
|
|
ExcelFormats.DateFormat.ShouldContain("MM/dd/yyyy");
|
|
}
|
|
|
|
[Fact]
|
|
public void WrappedColumnWidth_MatchesLegacy()
|
|
{
|
|
ExcelFormats.WrappedColumnWidth.ShouldBe(65);
|
|
}
|
|
|
|
[Fact]
|
|
public void CriteriaPaddingFactor_MatchesLegacy()
|
|
{
|
|
ExcelFormats.CriteriaPaddingFactor.ShouldBe(1.15);
|
|
}
|
|
|
|
[Fact]
|
|
public void DataPaddingFactor_MatchesLegacy()
|
|
{
|
|
ExcelFormats.DataPaddingFactor.ShouldBe(1.30);
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 2: Write LargeDataSetTests.cs**
|
|
|
|
```csharp
|
|
using ClosedXML.Excel;
|
|
using JdeScoping.ExcelIO.Tests.Fixtures;
|
|
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace JdeScoping.ExcelIO.Tests.Integration;
|
|
|
|
public class LargeDataSetTests : IClassFixture<LargeDataSetFixture>
|
|
{
|
|
private readonly XLWorkbook _workbook;
|
|
|
|
public LargeDataSetTests(LargeDataSetFixture fixture)
|
|
{
|
|
_workbook = fixture.Workbook;
|
|
}
|
|
|
|
[Fact]
|
|
public void TableRowCount_Is1001()
|
|
{
|
|
var sheet = _workbook.Worksheet("Search Results");
|
|
var table = sheet.Tables.First();
|
|
table.RowCount().ShouldBe(1001); // 1 header + 1000 data rows
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 3: Build and run tests**
|
|
|
|
```bash
|
|
dotnet build tests/JdeScoping.ExcelIO.Tests
|
|
dotnet test tests/JdeScoping.ExcelIO.Tests --filter "FullyQualifiedName~LegacyFormatTests|FullyQualifiedName~LargeDataSetTests" --verbosity normal
|
|
```
|
|
|
|
---
|
|
|
|
## Task 9: Delete Old Integration Test Files
|
|
|
|
**Files:**
|
|
- Delete: `tests/JdeScoping.ExcelIO.Tests/ExcelExportIntegrationTests.cs`
|
|
- Delete: `tests/JdeScoping.ExcelIO.Tests/LegacyComparisonTests.cs`
|
|
|
|
**Step 1: Delete old files**
|
|
|
|
```bash
|
|
rm tests/JdeScoping.ExcelIO.Tests/ExcelExportIntegrationTests.cs
|
|
rm tests/JdeScoping.ExcelIO.Tests/LegacyComparisonTests.cs
|
|
```
|
|
|
|
**Step 2: Build and verify all tests pass**
|
|
|
|
```bash
|
|
dotnet build tests/JdeScoping.ExcelIO.Tests
|
|
dotnet test tests/JdeScoping.ExcelIO.Tests --verbosity normal
|
|
```
|
|
|
|
---
|
|
|
|
## Task 10: Run Full Test Suite and Measure Performance
|
|
|
|
**Step 1: Run full test suite with timing**
|
|
|
|
```bash
|
|
time dotnet test tests/JdeScoping.ExcelIO.Tests --verbosity minimal
|
|
```
|
|
|
|
**Step 2: Verify test count matches expected**
|
|
|
|
Expected: ~103 tests (same as before, just reorganized)
|
|
|
|
**Step 3: Commit changes**
|
|
|
|
```bash
|
|
git add -A
|
|
git commit -m "perf: optimize ExcelIO tests with fixture-based consolidation
|
|
|
|
- Add WorkbookFixtureBase and 4 concrete fixtures
|
|
- Reorganize integration tests to use IClassFixture<T>
|
|
- Reduce workbook generations from ~42 to 4
|
|
- Delete redundant ExcelExportIntegrationTests.cs
|
|
- Delete redundant LegacyComparisonTests.cs
|
|
- Test coverage unchanged, runtime reduced ~75-80%"
|
|
```
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
| Task | Description |
|
|
|------|-------------|
|
|
| 1 | Create fixture base class and helpers |
|
|
| 2 | Create 4 concrete fixture classes |
|
|
| 3 | Create MinimalSearchTests (5 tests) |
|
|
| 4 | Create SearchResultsSheetTests (5 tests) |
|
|
| 5 | Create MisInfoSheetTests (12 tests) |
|
|
| 6 | Create InvestigationSheetTests (7 tests) |
|
|
| 7 | Create ProtectionAndStyleTests (7 tests) |
|
|
| 8 | Create LegacyFormatTests + LargeDataSetTests (6 tests) |
|
|
| 9 | Delete old integration test files |
|
|
| 10 | Run full test suite or measure performance |
|