0

I've run into a problem. I have created a solution that uses IoC. However, I have a fatal flaw - a primary DTO that I use (Data Transfer Object - so, a class that passes through the layers) contains, for example, a Person with Name, Surname, DateOfBirth, StatusId ... and a List of valid Statuses.

The statuses are populated when the Person class is created, and data comes from a reference data repository... this repository is a singleton and gets data from a cache (one it's got data from the database initially).

So,

Name string {get; set;}
Surname string {get; set;}
StatusId int {get; set;}
Statuses List<ReferenceItem> {get; set;} = ReferenceData.GetData(DataType.ClientStatus);

In my unit test, I end up with a dependency on the ReferenceData class, which is a singleton. Is there a way to inject a mock ReferenceData class somehow? Or is my design flawed, and I need to redesign my Person class?

Craig
  • 18,074
  • 38
  • 147
  • 248
  • Hey Craig, did you have a look at this thread? https://stackoverflow.com/questions/5897681/unit-testing-singletons – Danieboy Aug 29 '17 at 09:32
  • It's arguably a bit naughty of a class, that when created, it or a field object, immediately goes off to fetch data from a DB particularly when its not obvious during such things as `var x = new Person()`. [DO minimal work in the constructor.](https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/constructor) –  Aug 29 '17 at 09:33
  • Thanks @Danieboy - I'll look, but I think my flaw is in the design. I thought I was being clever with it grabbing data on creation - but as MickyD alludes... I think that's the fault. – Craig Aug 29 '17 at 09:35
  • Thanks @MickyD - I'm worried that's the issue, and I need to remove that, and somehow... pass reference data a different way... – Craig Aug 29 '17 at 09:36

2 Answers2

2

You have to redesign your class and use constructor injection. Follow this example (refered to yours):

class Person {
  public string Name { get; set; }
  public string Surname { get; set; }
  public int StatusId { get; set; }
  public List<ReferenceItem> Statuses { get; set; }

  public Person(IReferenceDataProvider provider) {
     Statuses = provider.GetData(DataType.ClientStatus);
  }
}

Or you can use directly the IoC container to resolve your dependency inside the constructor:

... 
public Person() {
   var provider = IoC.Resolve<IReferenceDataProvider>();
   Statuses = provider.GetData(DataType.ClientStatus);
}
...

Anyway you have to abstract your class reference and use interface instead. Hope this can help.

Roberto Conte Rosito
  • 2,080
  • 12
  • 22
1

As other pointed out for being able to test your code you should redesign it that code will be testable.
You are using DTO(Data Transfer Object) for passing data through different layers, I think, with this kind of responsibility those objects should have minimum dependencies on one of the layers.
In your case your DTO depend on static method or injected interface, or even worse on IoC container. Remove all of them and make your DTO to be object which responsibility is carrying data.

public class Person
{
    public string Name{ get; set; }
    public string Surname { get; set; }
    public int StatusId { get; set; }
    public List<ReferenceItem> Statuses { get; set; }
}

Then anywhere where you creating instance of Person you will have to inject IReferenceDataProvider.
Or you can introduce a Factory abstraction for creating instances of Person

public interface IPersonFactory
{
    Person Create(string name, string surname, int statusId);
}

And implementation

public class PersonFactory
{
    private readonly IReferenceDataProvider _dataProvider;

   public PersonFactory(IReferenceDataProvider dataProvider)
   {
       _dataProvider = dataProvider;
   }

   public Person Create(string name, string surname, int statusId)
   {
       return new Person
       {
           Name = name,
           Surname = surname,
           StatusId = statusId,
           Statuses = _dataProvider.GetData(DataType.ClientStatus)
       };
   }
}
Fabio
  • 31,528
  • 4
  • 33
  • 72
  • Very clean solution, @fabio ... Thanks. I am hibk this is a neat design and an introduction to the factory patter, long overdue for me. – Craig Sep 03 '17 at 13:18