On dirait que vous êtes dans l'état où j'étais il y a quelques années.
Note, si vous avez besoin d'aide supplémentaire, je peux vous envoyer du code. C'est juste difficile de mettre tout le code ici.
Je vais essayer d'expliquer l'architecture actuelle du projet sur lequel je travaille. Cet article est un peu long, mais j'essaie de vous donner une vue d'ensemble de la façon dont l'utilisation d'IOC peut vous aider à bien des égards.
J'utilise donc Ninject. Après avoir essayé d'utiliser Castle Windsor pendant un certain temps, j'ai trouvé Ninject facile à mettre en place et à utiliser. Ninject a un site web sympa qui vous aidera à démarrer.
Tout d'abord, la structure de mon projet est la suivante : (de haut en bas et c'est MVC)
Voir - rasoir ViewModel - J'utilise un modèle de vue par vue
ViewModelBuilder - Construit mes modèles de vue pour mes vues (utilisé pour abstraire le code de mon contrôleur afin que ce dernier reste propre et bien rangé).
AutoMapper - pour faire correspondre les entités du domaine à mes modèles de vue
Contrôleur - appelle ma couche de service pour obtenir les entités du domaine
Entités du domaine - représentations de mon domaine
ServiceLayer (couche métier) - Appelle ma couche de référentiel pour obtenir des entités de domaine ou des collections de celles-ci
AutoMapper à nouveau - pour faire correspondre les types personnalisés de mes fournisseurs tiers aux entités de mon domaine
Couche de dépôt - effectue des opérations CRUD sur mes magasins de données
Il s'agit d'une hiérarchie, mais les entités du domaine se situent en quelque sorte à côté et sont utilisées dans quelques couches différentes.
Note : certains outils supplémentaires mentionnés dans ce post sont :
AutoMapper - fait correspondre des entités à d'autres entités - élimine la nécessité d'écrire des tonnes de code de mise en correspondance
Moq - Permet de simuler des éléments pour les tests unitaires. Ceci est mentionné plus tard.
Maintenant, en ce qui concerne Ninject.
Chaque couche est marquée par une interface. Cela doit être fait pour que Ninject puisse se dire .
Lorsque je trouve IVehicleRepository, je l'injecte avec un vrai VehicleRepository ou même avec FakeVehicleRepository si j'ai besoin d'un faux.
(ceci est lié à votre commentaire - "Cela me permettra de tout lier à un référentiel XML de test")
Maintenant chaque couche a un contstructeur pour que Ninject (ou tout autre conteneur IOC) puisse injecter ce dont il a besoin :
public VehicleManager(IVehicleRepository vehicleRepository)
{
this._vehicleRepository = vehicleRepository;
}
VehicleManager se trouve dans ma serviceLayer (à ne pas confondre avec tout ce qui a trait aux services web). La couche service est en fait ce que nous appellerions la couche métier. Il semble que beaucoup de gens utilisent le mot service. (même si je pense que c'est ennuyeux car cela me fait penser à des services web ou WCF au lieu d'une simple couche métier.... de toute façon...)
Maintenant, sans entrer dans les détails de la configuration de Ninject, la ligne de code suivante dans mon NinjectWebCommon.cs indique à Ninject ce qu'il doit faire :
kernel.Bind<IVehicleRepository>().To<VehicleRepository>().InRequestScope();
Cela dit :
Hey Ninject, quand je demande IVehicleRepository donnez-moi une implémentation concrète de VehicleRepository.
Comme mentionné précédemment, je pourrais remplacer VehicleRepository par FakeVehicleRepository afin de ne pas avoir à lire depuis une vraie base de données.
Ainsi, comme vous pouvez maintenant l'imaginer, chaque couche ne dépend que des interfaces.
Je ne sais pas combien de tests unitaires vous avez fait, mais vous pouvez aussi imaginer que si vous vouliez tester unitairement votre couche de service et qu'elle avait des références concrètes à votre couche de référentiel, vous ne pourriez pas faire de tests unitaires car vous seriez en train de toucher RÉELLEMENT votre référentiel et donc de lire une base de données réelle.
N'oubliez pas que les tests unitaires sont appelés ainsi parce qu'ils ne testent qu'une seule chose. D'où le mot UNITE. Donc, parce que tout ne connaît que les interfaces, cela signifie que vous pouvez tester une méthode sur votre couche service et mocker le référentiel.
Donc si votre couche de service a une méthode comme celle-ci :
public bool ThisIsACar(int id)
{
bool isCar = false;
var vehicle = vehicleRepository.GetVehicleById(id);
if(vehicle.Type == VehicleType.Car)
{
isCar = true;
}
else
{
isCar = false;
}
}
Vous ne voudriez pas que le VehicleRepository soit appelé pour que vous puissiez Moq ce que le VehicleRepository vous rend. La plupart du temps, vous ne pouvez simuler des choses que si elles implémentent une interface.
So your unit test would look like this (some pseudo code here):
[TestMethod]
public void ServiceMethodThisIsACar_Returns_True_When_VehicleIsACar()
{
// Arrange
_mockRepository.Setup(x => x.ThisIsACar(It.IsAny<int>)).returns(new Car with a type of VehicleType.Car)
// Act
var vehicleManager = new VehicleManager(_mockVehicleRepository.Object);
var vehicle = vehicleManager.ThisIsACar(3);
// Assert
Assert.IsTrue(vehicle.VehicleType == VehicleType.Car)
}
Comme vous pouvez le voir, à ce stade, et c'est très simplifié, vous voulez seulement tester l'instruction IF dans votre couche service pour vous assurer que le résultat est correct.
Vous testerez votre référentiel dans ses propres tests unitaires et vous pourrez éventuellement simuler la structure de l'entité si vous l'utilisez.
Donc, dans l'ensemble, je dirais qu'il faut utiliser le conteneur IOC qui vous permet d'être opérationnel le plus rapidement et avec le moins de douleur possible.
Je dirais aussi, essayez de tester en unité tout ce que vous pouvez. C'est génial pour plusieurs raisons différentes. Évidemment, cela permet de tester le code que vous avez écrit, mais cela vous montrera aussi immédiatement si vous avez fait quelque chose de stupide, comme créer un dépôt concret. Vous verrez rapidement que vous n'avez pas d'interface à simuler dans vos tests unitaires et cela vous amènera à revenir en arrière et à remanier votre code.
J'ai découvert qu'avec le CIO, il faut un certain temps pour comprendre. Ça vous perturbe jusqu'à ce qu'un jour vous ayez le déclic. Après cela, c'est tellement facile que vous vous demandez comment vous avez pu vivre sans.
Voici une liste de choses dont je ne peux pas me passer Automapper Moq Fluent Validation Resharper - certains le détestent, moi je l'adore, surtout pour son interface utilisateur de test unitaire.
Bref, ça devient trop long. Dites-moi ce que vous en pensez.
Merci RuSs