Search code examples
c#abstract-classabstract

Populating a field that has no setter in abstract class


I'm working with a class EventRecordWrittenEventArgs in my unit test(s):

public sealed class EventRecordWrittenEventArgs : EventArgs {
  public EventRecord EventRecord {get;}
  public Exception EventException {get;}
}

I would like to populate the EventException and EventRecord fields with my own custom data so I've created my own version of the above class called EventRecordWrittenEventArgsAdaptor that just has a setter in addition to getter for each field.

Problem is, EventRecord is an abstract class with no setters which would mean I can't populate my own custom EventRecord object (which is ultimately my goal):

public abstract class EventRecord : IDisposable {
  public abstract Guid? ProviderId {get;}
  public abstract string TaskDisplayName {get;}
  public abstract string OpcodeDispayName {get;}
  ...
}

My first thought was to make a EventRecordAdaptor class that derives from EventRecord and give it each field a setter but that doesn't work:

public class EventRecordAdaptor : EventRecord {
  public override Guid? ProviderId {get;set;} // "cannot override because EventRecord.Guid does not have an overrideable set accessor"
}

How am I supposed to create my own EventRecord object that I can populate with whatever I want? To be clear, my end goal is to have the ability to fill EventRecord and EventException with whatever I want for the purpose of unit testing. Surely there's a way to populate this field?


Solution

  • The constructor is internal, so you basically can't use EventRecord directly. This is common when using external APIs. In situations like this, your code should not use the native .NET type, but wrap it in an interface that you can mock. There are lots of Nuget libraries where people have created proxies for various .NET classes, and one for the EventRecord may already exist that you can use. If not, writing your own is straightforward.

    // this is the interface your code will use *instead of* EventRecord
    // that exposes the properties and methods your code needs
    public interface IEventRecord
    {
        public Guid? ProviderId {get;}
        public string TaskDisplayName {get;}
        public string OpcodeDispayName {get;}
    }
    
    // this is the concrete type you'll use in your production code
    // to turn .NET's EventRecord into an IEventRecord
    public EventRecordProxy : IEventRecord
    { 
        private EventRecord _eventRecord;
    
        public Guid? ProviderId =>  _eventRecord.ProviderId;
        public string TaskDisplayName => _eventRecord.TaskDisplayName;
        public string OpcodeDisplayName => _eventRecord.OpcodeDisplayName;
    
        public EventRecordProxy(EventRecord er)
        {
            _eventRecord = er;
        }
    }
    
    // this is the concrete type you'll use in your tests, with accessible properties
    // (or if you're using a mocking framework, you can mock IEventRecord directly.)
    public TestEventRecordProxy : IEventRecord
    {
        public Guid? ProviderId { get; set; }
        public string TaskDisplayName { get; set}
        public string OpcodeDispayName { get; set;}
    }
    

    Yes, this is a hassle at first, but it's a far more flexible and modular design in general. You are no longer tied at all to the specifics of EventRecord. If you decide to get that data somewhere else, or composite multiple objects together, you can that do that easily now.