Home > Back-end >  Populating a field that has no setter in abstract class
Populating a field that has no setter in abstract class

Time:04-08

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?

CodePudding user response:

I am not sure about your project constraints. If you can make below changes to abstract class , it is possible to pass GUID to EventRecord , as shown below.

public abstract class EventRecord : IDisposable
{
  private readonly Guid _id;

  public EventRecord(Guid id)
   {
     _id = id;
   }

  public virtual Guid? ProviderId { get => _id; }
  public abstract string TaskDisplayName { get; }
 ......
 }

public class EventRecordAdaptor : EventRecord
{
  public EventRecordAdaptor(Guid id) : base(id)
  {
  }
    public override Guid? ProviderId => base.ProviderId;
    ...........
}

CodePudding user response:

Based on kirk-woll's comment:

public class EventRecordAdaptor : EventRecord
{
    public EventRecordAdaptor(Guid? id)
    {
        _activityId = id;
    }

    private Guid? _activityId;

    public override Guid? ActivityId => _activityId;

    ...

}

CodePudding user response:

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.

  • Related