Service for generating faked objects
public interface IFaker&lt;T&gt; where T : class
{
    Faker&lt;T&gt; GetFaker();
}



I don't think you need this interface and the additional call to GetFaker. Instead you can derive your class from the Faker&lt;T&gt; and use the constructor to set everything up:

public class DogFaker : Faker&lt;Dog&gt;
{
    public DogFaker()
    {
        RuleFor(dog =&gt; dog.Name, f =&gt; f.Name.FirstName(Name.Gender.Male));
        RuleFor(dog =&gt; dog.IsGoodBoy, f =&gt; f.Random.Bool());
    }   
}





private Faker&lt;T&gt; GetFaker&lt;T&gt;() where T : class
{
  if (!_fakers.ContainsKey(typeof(T)))
  {
      throw new ArgumentException($"Type: {typeof(T).FullName} not registered with FakerService");
  }
  var faker = (Faker&lt;T&gt;)_fakers[typeof(T)];
  return faker;
}



In this case it would be much clener to query the dictionary only once with TryGetValue if you are going to use the value anyway rather then doing it twice, once with ContainsKey and then with the [] indexer. At the same time you can tun it into a ternary operator ?::

private Faker&lt;T&gt; GetFaker&lt;T&gt;() where T : class
{
    return
        _fakers.TryGetValue(typeof(T), out var faker)
            ? (Faker&lt;T&gt;)faker
            : throw new ArgumentException($"Type: {typeof(T).FullName} not registered with FakerService");
}




Since ´InitialiseFakers´ does not require any instance data I suggest making it static. I like the way you created this initializer. Instead of letting it set the _fakers dictionary you return it as a result and assign it in the construtor. This way it's much cleaner and all the initialization-assignment stays inside the construtor so a big plus for that.




// Can we do better than &lt;Type, object&gt; ?
private Dictionary&lt;Type, object&gt; _fakers;



I'm afraid we cannot because Faker&lt;T&gt; isn't derived from any other collection-friendly interface. All you have is:


public class Faker&lt;T&gt; : IFakerTInternal, ILocaleAware, IRuleSet&lt;T&gt; where T : class



and this is not very useful so object is the only possible generalisation in this case. But this isn't that bad. It's an object so it doesn't cost you a penny to cast it. But sure, some general interface would be nice.