Skip to content

Support wrapping a concrete instance #181

@vbreuss

Description

@vbreuss

Description

Support the ability to wrap a concrete instance in Mockolate, similar to the behavior described in FakeItEasy's "calling wrapped methods" documentation.

Developers should be able to use Mockolate not only for interfaces or abstract classes but also to wrap existing concrete objects. This allows for interception, verification, and stubbing of behavior in already-created instances.

Expected Behavior

  • Allow an existing concrete object to be passed to Mockolate for wrapping
  • Mockolate should intercept and verify calls to methods on the wrapped instance
  • Non-overridden methods should delegate to the original underlying object
  • Test doubles can be created that wrap and extend the behavior of a concrete instance
  • Clearly document the API for creating a wrapped instance and the limitations or caveats involved

Implementation Plan: Support Wrapping a Concrete Instance (Mockolate)

1. API Surface Proposal

public static class Mock
{
    /// <summary>
    /// Wraps a concrete instance with a mock proxy that intercepts and delegates method calls,
    /// supporting setup and verification on the wrapped instance.
    /// </summary>
    public static T Wrap<T>(T instance) where T : class;
}
  • Usage Example:
    var myInstance = new MyService();
    var wrapped = Mock.Wrap(myInstance);
    
    // Setup example: Override method behavior via SetupMock
    wrapped.SetupMock.Method.DoSomething()
           .Returns(() => "Mocked Value");
    
    // Use original implementation unless overridden
    var result = wrapped.OtherMethod(); // delegates to myInstance.OtherMethod()
    
    // Verification example: Check interactions
    wrapped.VerifyMock.Invoked.DoSomething().Exactly(1);

2. Proxy Generation

  • Use proxy or source generator techniques to create a wrapper that intercepts calls:
    • For wrapped methods, use the override from SetupMock, otherwise delegate to the original instance.
    • Use event and property interception if supported/required.

3. Setup and Verification Syntax

  • Setup:

    • Use .SetupMock fluent APIs to override specific behavior (e.g. methods, properties, indexers).
    • Supports argument matchers, call count limits, and custom return delegates.
  • Verification:

    • Use .VerifyMock for call assertions and invocation matching.
    • Standard pattern for asserting invocations and argument matching.

4. Delegation Rules

  • Unless overridden with .SetupMock, calls and property accesses are delegated to the original object.
  • Document any limitations—e.g., sealed/non-virtual methods, static members.

5. Testing

  • Ensure comprehensive tests for:
    • Wrapping and interacting with various types (interfaces, concrete classes, inherited types).
    • Overriding via .SetupMock, verifying via .VerifyMock.
    • Edge cases coverage.

6. Documentation

  • Provide examples using the above API, matching the README style.
  • Clarify caveats of concrete instance wrapping.

Example Usage in Mockolate Syntax

var service = new MyService();
var wrapped = Mock.Wrap(service);

// Setup override
wrapped.SetupMock.Method.GetData().Returns(() => "MockedData");

// Unmocked methods delegate to service
wrapped.SetupMock.Method.Add(2, 3).KeepOriginalBehavior();

var result = wrapped.Add(2, 3); // Calls original instance
var data = wrapped.GetData(); // Returns "MockedData"

// Verify method invocations
wrapped.VerifyMock.Invoked.GetData().Exactly(1);

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions