Index: DEPENDENCIES.md =================================================================== diff -u -r1bb6c7bb53b378ce4f48f21a35caba0aab4a7e4b -r09baf8dc969f6901a51bda3d65b8a206ba2f3b8b --- DEPENDENCIES.md (.../DEPENDENCIES.md) (revision 1bb6c7bb53b378ce4f48f21a35caba0aab4a7e4b) +++ DEPENDENCIES.md (.../DEPENDENCIES.md) (revision 09baf8dc969f6901a51bda3d65b8a206ba2f3b8b) @@ -44,6 +44,7 @@ | OxyPlot.WindowsForms | 1.0.0 | MIT | https://github.com/oxyplot/oxyplot | | Piping | 16.2.1.4574 | AGPL-3.0 | https://repos.deltares.nl/repos/FailureMechanisms/FailureMechanisms/DikesPiping | | QuickGraph | 3.6.61119.7 | MS-PL | https://www.nuget.org/packages/QuickGraph | +| NSubstitute | 4.4.0 | BSD-3-Clause | https://github.com/nsubstitute/NSubstitute | | RhinoMocks | 3.6.1 | BSD-3-Clause | https://github.com/hibernating-rhinos/rhino-mocks | | SmartThreadPool.dll | 2.2.4 | MS-PL | https://github.com/amibar/SmartThreadPool | | Stub.System.Data.SQLite.Core.NetFramework | 1.0.117 | MS-PL | https://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki | Index: Riskeer/Common/test/Riskeer.Common.Data.TestUtil/AssessmentSectionTestHelper.cs =================================================================== diff -u -r9339a780307cdb21ebe38cbd3aa8811e2c98d980 -r09baf8dc969f6901a51bda3d65b8a206ba2f3b8b --- Riskeer/Common/test/Riskeer.Common.Data.TestUtil/AssessmentSectionTestHelper.cs (.../AssessmentSectionTestHelper.cs) (revision 9339a780307cdb21ebe38cbd3aa8811e2c98d980) +++ Riskeer/Common/test/Riskeer.Common.Data.TestUtil/AssessmentSectionTestHelper.cs (.../AssessmentSectionTestHelper.cs) (revision 09baf8dc969f6901a51bda3d65b8a206ba2f3b8b) @@ -25,6 +25,7 @@ using System.Linq; using Core.Common.Base.Data; using Core.Common.TestUtil; +using NSubstitute; using Rhino.Mocks; using Riskeer.Common.Data.AssessmentSection; using Riskeer.Common.Data.Contribution; @@ -44,8 +45,21 @@ /// /// Creates a stub of with that is not linked. /// - /// The mock repository to create the stub with. /// A stubbed . + public static IAssessmentSection CreateAssessmentSectionStub() + { + var assessmentSection = Substitute.For(); + assessmentSection.HydraulicBoundaryData.Returns(new HydraulicBoundaryData()); + assessmentSection.ReferenceLine.Returns(new ReferenceLine()); + + return assessmentSection; + } + + /// + /// Creates a stub of with that is not linked. + /// + /// The Rhino mock repository to create the stub with. + /// A stubbed . public static IAssessmentSection CreateAssessmentSectionStub(MockRepository mockRepository) { var assessmentSection = mockRepository.Stub(); @@ -60,7 +74,6 @@ /// Creates a stub of . /// /// The failure mechanism to set the contribution for. - /// The mock repository to create the stub with. /// The path to the hydraulic boundary database (optional). /// Whether or not to use preprocessor closure (optional). /// A stubbed . @@ -71,6 +84,30 @@ /// /// public static IAssessmentSection CreateAssessmentSectionStub(IFailureMechanism failureMechanism, + string hrdFilePath = null, + bool usePreprocessorClosure = false) + { + IFailureMechanism[] failureMechanisms = GetFailureMechanisms(failureMechanism); + + var assessmentSection = Substitute.For(); + assessmentSection.Id.Returns("21"); + assessmentSection.FailureMechanismContribution.Returns(new FailureMechanismContribution(0.1, 1.0 / 30000)); + assessmentSection.GetFailureMechanisms().Returns(failureMechanisms); + assessmentSection.HydraulicBoundaryData.Returns(GetHydraulicBoundaryData(hrdFilePath, usePreprocessorClosure)); + assessmentSection.ReferenceLine.Returns(new ReferenceLine()); + + return assessmentSection; + } + + /// + /// Creates a stub of . + /// + /// The failure mechanism to set the contribution for. + /// The Rhino mock repository to create the stub with. + /// The path to the hydraulic boundary database (optional). + /// Whether or not to use preprocessor closure (optional). + /// A stubbed . + public static IAssessmentSection CreateAssessmentSectionStub(IFailureMechanism failureMechanism, MockRepository mockRepository, string hrdFilePath = null, bool usePreprocessorClosure = false) Index: Riskeer/Common/test/Riskeer.Common.Data.TestUtil/Riskeer.Common.Data.TestUtil.csproj =================================================================== diff -u -re3d1ad7847ae5897d8764d49a2be8faa1490dc9e -r09baf8dc969f6901a51bda3d65b8a206ba2f3b8b --- Riskeer/Common/test/Riskeer.Common.Data.TestUtil/Riskeer.Common.Data.TestUtil.csproj (.../Riskeer.Common.Data.TestUtil.csproj) (revision e3d1ad7847ae5897d8764d49a2be8faa1490dc9e) +++ Riskeer/Common/test/Riskeer.Common.Data.TestUtil/Riskeer.Common.Data.TestUtil.csproj (.../Riskeer.Common.Data.TestUtil.csproj) (revision 09baf8dc969f6901a51bda3d65b8a206ba2f3b8b) @@ -21,6 +21,12 @@ 3.8.1 + + 4.4.0 + + + 1.0.16 + 3.6.1 Index: Riskeer/Common/test/Riskeer.Common.IO.TestUtil/CustomCalculationConfigurationWriterDesignGuidelinesTestFixture.cs =================================================================== diff -u -r9339a780307cdb21ebe38cbd3aa8811e2c98d980 -r09baf8dc969f6901a51bda3d65b8a206ba2f3b8b --- Riskeer/Common/test/Riskeer.Common.IO.TestUtil/CustomCalculationConfigurationWriterDesignGuidelinesTestFixture.cs (.../CustomCalculationConfigurationWriterDesignGuidelinesTestFixture.cs) (revision 9339a780307cdb21ebe38cbd3aa8811e2c98d980) +++ Riskeer/Common/test/Riskeer.Common.IO.TestUtil/CustomCalculationConfigurationWriterDesignGuidelinesTestFixture.cs (.../CustomCalculationConfigurationWriterDesignGuidelinesTestFixture.cs) (revision 09baf8dc969f6901a51bda3d65b8a206ba2f3b8b) @@ -25,8 +25,8 @@ using System.Security.AccessControl; using Core.Common.IO.Exceptions; using Core.Common.TestUtil; +using NSubstitute; using NUnit.Framework; -using Rhino.Mocks; using Riskeer.Common.IO.Configurations; using Riskeer.Common.IO.Configurations.Export; @@ -41,9 +41,7 @@ public void Write_CalculationOfTypeOtherThanGiven_ThrowsCriticalFileWriteExceptionWithInnerArgumentException() { // Setup - var mocks = new MockRepository(); - var calculation = mocks.Stub(); - mocks.ReplayAll(); + var calculation = Substitute.For(); string filePath = TestHelper.GetScratchPadPath("test.xml"); @@ -67,8 +65,6 @@ { File.Delete(filePath); } - - mocks.VerifyAll(); } [Test] Index: Riskeer/Common/test/Riskeer.Common.IO.TestUtil/Riskeer.Common.IO.TestUtil.csproj =================================================================== diff -u -r08e8d26a0715f0f3db57c1d3e86256aa06934db4 -r09baf8dc969f6901a51bda3d65b8a206ba2f3b8b --- Riskeer/Common/test/Riskeer.Common.IO.TestUtil/Riskeer.Common.IO.TestUtil.csproj (.../Riskeer.Common.IO.TestUtil.csproj) (revision 08e8d26a0715f0f3db57c1d3e86256aa06934db4) +++ Riskeer/Common/test/Riskeer.Common.IO.TestUtil/Riskeer.Common.IO.TestUtil.csproj (.../Riskeer.Common.IO.TestUtil.csproj) (revision 09baf8dc969f6901a51bda3d65b8a206ba2f3b8b) @@ -21,8 +21,11 @@ 3.8.1 - - 3.6.1 + + 4.4.0 + + 1.0.16 + \ No newline at end of file Index: plan-replaceRhinoMocksWithNSubstitute.prompt.md =================================================================== diff -u -r2b7b6c53b3a7b69bc318e36db92c8dd6131413a1 -r09baf8dc969f6901a51bda3d65b8a206ba2f3b8b --- plan-replaceRhinoMocksWithNSubstitute.prompt.md (.../plan-replaceRhinoMocksWithNSubstitute.prompt.md) (revision 2b7b6c53b3a7b69bc318e36db92c8dd6131413a1) +++ plan-replaceRhinoMocksWithNSubstitute.prompt.md (.../plan-replaceRhinoMocksWithNSubstitute.prompt.md) (revision 09baf8dc969f6901a51bda3d65b8a206ba2f3b8b) @@ -1,15 +1,21 @@ ## Plan: Replace Rhino.Mocks with NSubstitute -Migrate test projects from Rhino.Mocks to NSubstitute in controlled phases: establish an API translation baseline, convert shared test utilities first, run pilot conversions in high-usage test suites, then scale by domain and finally remove Rhino package references. This reduces regression risk around strict/replay semantics while keeping CI green and making future test maintenance easier. +Migrate test projects from Rhino.Mocks to NSubstitute in controlled phases using a dual-stack transition: shared test helpers expose both Rhino.Mocks and NSubstitute implementations until repository usage of Rhino is zero. This reduces regression risk around strict/replay semantics while keeping CI green and allowing incremental suite migration. ### Steps 1. [ ] Inventory Rhino usage and package references across `Riskeer/**/*.cs` and `Riskeer/**/*.csproj`, tagging `MockRepository`, `Expect`, `Stub`, `Arg`, `ReplayAll`, `VerifyAll`, `WhenCalled`, `IgnoreArguments`, `Repeat`. 2. [ ] Define and publish a migration rulebook in `DEPENDENCIES.md` with approved `NSubstitute` patterns and unsupported Rhino idioms. -3. [ ] Convert shared helpers first (for example `Riskeer/Common/test/Riskeer.Common.Data.TestUtil/AssessmentSectionTestHelper.cs`) to stop propagating `MockRepository` usage. +3. [ ] Convert shared helpers first (for example `Riskeer/Common/test/Riskeer.Common.Data.TestUtil/AssessmentSectionTestHelper.cs`) to dual-stack helpers: keep Rhino-based overloads and add/keep NSubstitute overloads. 4. [ ] Execute a pilot in two heavy suites (`Riskeer/WaveImpactAsphaltCover/test/Riskeer.WaveImpactAsphaltCover.Service.Test`, `Riskeer/Common/test/Riskeer.Common.IO.Test`), then refine rules from real failures. 5. [ ] Roll out by domain test folders (Service -> Plugin -> Integration -> Forms/Data), updating each `*.csproj` from `RhinoMocks` to `NSubstitute`. -6. [ ] Remove remaining Rhino references and enforce a gate that blocks new `using Rhino.Mocks;` in `Riskeer.sln` scope. +6. [ ] When Rhino usage reaches zero in tests, remove Rhino helper overloads and Rhino package references, then enforce a gate that blocks new `using Rhino.Mocks;` in `Riskeer.sln` scope. +### Dual-Stack Policy (Temporary) +1. Test helper projects may reference both `RhinoMocks` and `NSubstitute` during migration. +2. Helper APIs must keep Rhino-compatible overloads for existing tests and NSubstitute-first overloads for new tests. +3. No helper should remove Rhino overloads until repository-wide Rhino call sites are zero. +4. Per migration batch, Rhino usage must be non-increasing. + ### API Mapping | Rhino.Mocks pattern | NSubstitute equivalent | Notes | |---|---|---| @@ -26,17 +32,17 @@ ### Phased Rollout 1. **Phase 0 - Baseline:** create inventory and migration rulebook; freeze new Rhino usage. -2. **Phase 1 - Foundations:** migrate shared `*TestUtil` helpers and common fixtures first. -3. **Phase 2 - Pilot:** migrate selected high-usage suites, capture edge cases (strict expectations, callbacks, argument matching). -4. **Phase 3 - Scale-out:** migrate remaining test projects by bounded domain batches with small PRs. -5. **Phase 4 - Decommission:** remove `RhinoMocks` package references and add policy checks preventing reintroduction. +2. **Phase 1 - Foundations (Dual-Stack):** migrate shared `*TestUtil` helpers and common fixtures to provide both Rhino and NSubstitute implementations. +3. **Phase 2 - Pilot:** migrate selected high-usage suites to NSubstitute, capture edge cases (strict expectations, callbacks, argument matching), while helpers remain dual-stack. +4. **Phase 3 - Scale-out:** migrate remaining test projects by bounded domain batches with small PRs; track Rhino burn-down per batch. +5. **Phase 4 - Decommission:** after Rhino usage is zero, remove Rhino helper overloads and `RhinoMocks` package references, then add policy checks preventing reintroduction. ### Verification Gates 1. **Gate A (Baseline):** Rhino usage inventory approved; all migration rules mapped for discovered APIs. -2. **Gate B (Per phase):** no `using Rhino.Mocks;` in migrated scope; project builds and its tests pass. +2. **Gate B (Per phase):** Rhino usage in migrated scope is non-increasing; project builds and its tests pass. 3. **Gate C (Pilot exit):** translated callback/argument/count patterns validated in pilot suites. -4. **Gate D (Rollout exit):** all targeted `*.csproj` use `NSubstitute`; no `RhinoMocks` `PackageReference` remains. -5. **Gate E (Final):** repository-wide search finds zero Rhino APIs and CI remains green for full test matrix. +4. **Gate D (Rollout exit):** all targeted migrated suites use `NSubstitute`; remaining Rhino usage is explicitly tracked and accepted only in not-yet-migrated suites/helpers. +5. **Gate E (Final):** repository-wide search finds zero Rhino APIs and zero Rhino package references, then CI remains green for full test matrix. ### Further Considerations 1. Should strict-mock intent be preserved exactly (Option A) or simplified to intent-focused `Received()` assertions (Option B)?