J'ai un constructeur qui a une dépendance non-interface:AutoMockContainer avec le soutien pour les classes de automocking avec dépendances non l'interface
public MainWindowViewModel(IWorkItemProvider workItemProvider, WeekNavigatorViewModel weekNavigator)
J'utilise le automockcontainer Moq.Contrib. Si j'essaye d'automock la classe MainWindowViewModel, j'obtiens une erreur due à la dépendance de WeekNavigatorViewModel.
Existe-t-il des conteneurs d'automatisation qui prennent en charge les types non-interface?
Comme l'a montré Mark ci-dessous; Oui, vous pouvez! :-) J'ai remplacé le Moq.Contrib AutoMockContainer par le contenu que Mark présente dans sa réponse, la seule différence est que les mocks générés automatiquement sont enregistrés en tant que singletons, mais vous pouvez le rendre configurable. Voici la solution finale:
/// <summary>
/// Auto-mocking factory that can create an instance of the
/// class under test and automatically inject mocks for all its dependencies.
/// </summary>
/// <remarks>
/// Mocks interface and class dependencies
/// </remarks>
public class AutoMockContainer
{
readonly IContainer _container;
public AutoMockContainer(MockFactory factory)
{
var builder = new ContainerBuilder();
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
builder.RegisterSource(new MoqRegistrationSource(factory));
_container = builder.Build();
}
/// <summary>
/// Gets or creates a mock for the given type, with
/// the default behavior specified by the factory.
/// </summary>
public Mock<T> GetMock<T>() where T : class
{
return (_container.Resolve<T>() as IMocked<T>).Mock;
}
/// <summary>
/// Creates an instance of a class under test,
/// injecting all necessary dependencies as mocks.
/// </summary>
/// <typeparam name="T">Requested object type.</typeparam>
public T Create<T>() where T : class
{
return _container.Resolve<T>();
}
public T Resolve<T>()
{
return _container.Resolve<T>();
}
/// <summary>
/// Registers and resolves the given service on the container.
/// </summary>
/// <typeparam name="TService">Service</typeparam>
/// <typeparam name="TImplementation">The implementation of the service.</typeparam>
public void Register<TService, TImplementation>()
{
var builder = new ContainerBuilder();
builder.RegisterType<TImplementation>().As<TService>().SingleInstance();
builder.Update(_container);
}
/// <summary>
/// Registers the given service instance on the container.
/// </summary>
/// <typeparam name="TService">Service type.</typeparam>
/// <param name="instance">Service instance.</param>
public void Register<TService>(TService instance)
{
var builder = new ContainerBuilder();
if (instance.GetType().IsClass)
builder.RegisterInstance(instance as object).As<TService>();
else
builder.Register(c => instance).As<TService>();
builder.Update(_container);
}
class MoqRegistrationSource : IRegistrationSource
{
private readonly MockFactory _factory;
private readonly MethodInfo _createMethod;
public MoqRegistrationSource(MockFactory factory)
{
_factory = factory;
_createMethod = factory.GetType().GetMethod("Create", new Type[] { });
}
public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
var swt = service as IServiceWithType;
if (swt == null)
{
yield break;
}
if (!swt.ServiceType.IsInterface)
yield break;
var existingReg = registrationAccessor(service);
if (existingReg.Any())
{
yield break;
}
var reg = RegistrationBuilder.ForDelegate((c, p) =>
{
var createMethod = _createMethod.MakeGenericMethod(swt.ServiceType);
return ((Mock)createMethod.Invoke(_factory, null)).Object;
}).As(swt.ServiceType).SingleInstance().CreateRegistration();
yield return reg;
}
public bool IsAdapterForIndividualComponents
{
get { return false; }
}
}
}
Est-ce que cela fonctionne si MyClass a une dépendance sur un type concret au lieu d'une interface? – Marius
Oui, 'AnyConcreteTypeNotAlreadyRegisteredSource' d'Autofac s'occupe de la résolution des types concrets. –
Je pense que l'enregistrement Mock devrait être explicitement défini comme 'SingleInstance' dans cette instruction' As (swt.ServiceType) .CreateRegistration() '. J'ai eu un problème avec NSubstitute, si ce n'est pas 'SingleInstance' il pourrait être problématique. Par exemple: 'Resolver.Resolve(). Get (" 1 "). Returns (nouveau Basket (1, 50));' ici 'ICacheManager' créé par conteneur auto-substitué et s'il est transitoire' Retourne 'ne sera jamais défini comme prévu car il s'agit d'une nouvelle instance utilisant' var sut = Resolver.Resolve (); 'ici, IOrderService utilise une nouvelle dépendance' ICacheManager' substituée. –