diff --git a/Docs/pages/02-setup.md b/Docs/pages/02-setup.md index 0fd64d46..90140106 100644 --- a/Docs/pages/02-setup.md +++ b/Docs/pages/02-setup.md @@ -161,3 +161,33 @@ sut.SetupMock.Indexer(It.Is("Dark")) **Note**: You can use the same [parameter matching](#parameter-matching) and [interaction](#parameter-interaction) options as for methods. + +## Working with Protected Members + +Mockolate allows you to set up and verify protected methods and properties on class mocks. Access protected members using the `.Protected` property: + +```csharp +public abstract class ChocolateDispenser +{ + protected virtual bool DispenseInternal(string type, int amount) => true; + protected virtual int InternalStock { get; set; } +} + +var sut = Mock.Create(); + +// Setup protected method +sut.SetupMock.Protected.Method.DispenseInternal( + It.Is("Dark"), It.IsAny()) + .Returns(true); + +// Setup protected property +sut.SetupMock.Protected.Property.InternalStock.InitializeWith(100); +``` + +**Notes:** +- Protected members can be set up and verified just like public members, using the `.Protected` accessor. +- All setup options (`.Returns()`, `.Throws()`, `.Do()`, `.InitializeWith()`, etc.) work with protected members. +- All verification options (`.Once()`, `.AtLeastOnce()`, etc.) work with protected members. +- Protected indexers are supported using `.Protected.Indexer()` for setup and `.GotProtectedIndexer()`/`.SetProtectedIndexer()` for verification. +- For verification examples, see the Protected Members section in the verify interactions documentation. + diff --git a/Docs/pages/04-verify-interactions.md b/Docs/pages/04-verify-interactions.md index ba095886..7dae79e9 100644 --- a/Docs/pages/04-verify-interactions.md +++ b/Docs/pages/04-verify-interactions.md @@ -99,6 +99,31 @@ sut.VerifyMock.SubscribedTo.ChocolateDispensed().AtLeastOnce(); sut.VerifyMock.UnsubscribedFrom.ChocolateDispensed().Once(); ``` +## Protected Members + +You can verify interactions with protected members on class mocks using the `.Protected` accessor: + +```csharp +// Verify protected method was invoked +sut.VerifyMock.Invoked.Protected.DispenseInternal( + It.Is("Dark"), It.IsAny()).Once(); + +// Verify protected property was read +sut.VerifyMock.Got.Protected.InternalStock().AtLeastOnce(); + +// Verify protected property was set +sut.VerifyMock.Set.Protected.InternalStock(It.Is(100)).Once(); + +// Verify protected indexer was read +sut.VerifyMock.GotProtectedIndexer(It.Is(0)).Once(); + +// Verify protected indexer was set +sut.VerifyMock.SetProtectedIndexer(It.Is(0), It.Is(42)).Once(); +``` + +**Note:** +All verification options (argument matchers, count assertions) work the same for protected members as for public members. + ## Call Ordering Use `Then` to verify that calls occurred in a specific order: diff --git a/README.md b/README.md index 183d0a8e..0130b283 100644 --- a/README.md +++ b/README.md @@ -316,6 +316,35 @@ sut.SetupMock.Indexer(It.Is("Dark")) You can use the same [parameter matching](#parameter-matching) and [interaction](#parameter-interaction) options as for methods. +### Working with Protected Members + +Mockolate allows you to set up and verify protected methods and properties on class mocks. Access protected members using the `.Protected` property: + +```csharp +public abstract class ChocolateDispenser +{ + protected virtual bool DispenseInternal(string type, int amount) => true; + protected virtual int InternalStock { get; set; } +} + +var sut = Mock.Create(); + +// Setup protected method +sut.SetupMock.Protected.Method.DispenseInternal( + It.Is("Dark"), It.IsAny()) + .Returns(true); + +// Setup protected property +sut.SetupMock.Protected.Property.InternalStock.InitializeWith(100); +``` + +**Notes:** +- Protected members can be set up and verified just like public members, using the `.Protected` accessor. +- All setup options (`.Returns()`, `.Throws()`, `.Do()`, `.InitializeWith()`, etc.) work with protected members. +- All verification options (`.Once()`, `.AtLeastOnce()`, etc.) work with protected members. +- Protected indexers are supported using `.Protected.Indexer()` for setup and `.GotProtectedIndexer()`/`.SetProtectedIndexer()` for verification. +- See [Protected Members](#protected-members) in the Verify interactions section for verification examples. + ## Mock events Easily raise events on your mock to test event handlers in your code. @@ -453,6 +482,31 @@ sut.VerifyMock.SubscribedTo.ChocolateDispensed().AtLeastOnce(); sut.VerifyMock.UnsubscribedFrom.ChocolateDispensed().Once(); ``` +### Protected Members + +You can verify interactions with protected members on class mocks using the `.Protected` accessor: + +```csharp +// Verify protected method was invoked +sut.VerifyMock.Invoked.Protected.DispenseInternal( + It.Is("Dark"), It.IsAny()).Once(); + +// Verify protected property was read +sut.VerifyMock.Got.Protected.InternalStock().AtLeastOnce(); + +// Verify protected property was set +sut.VerifyMock.Set.Protected.InternalStock(It.Is(100)).Once(); + +// Verify protected indexer was read +sut.VerifyMock.GotProtectedIndexer(It.Is(0)).Once(); + +// Verify protected indexer was set +sut.VerifyMock.SetProtectedIndexer(It.Is(0), It.Is(42)).Once(); +``` + +**Note:** +All verification options (argument matchers, count assertions) work the same for protected members as for public members. + ### Call Ordering Use `Then` to verify that calls occurred in a specific order: