Pourquoi la propriété que je veux simuler doit-elle être virtuelle?

Je fais des tests unitaires et je me moque de certaines propriétés avec Moq .

Il s’agit d’un test de contrôleur (ASP.NET MVC 3). Mes contrôleurs proviennent d’un contrôleur abstrait , appelé AbstractController .

Ce contrôleur dépend du contexte Http (pour effectuer des opérations telles que la thématisation, une logique spécifique à un domaine basée sur des en-têtes HTTP HOST, etc.)

Cela se fait via une propriété appelée WebSiteSettings :

public abstract class AbstractController : Controller { public WebSiteSettings WebSiteSettings { get; private set; } // other code } 

Remarquez le set privé – le ctor le configure. Donc, je l’ai changé pour utiliser une interface, et c’est ce que je me suis moqué:

 public IWebSiteSettings WebSiteSettings { get; private set; } 

J’ai ensuite créé un “FakeWebSiteSettings”, qui se moque du contexte Http pour lui permettre de lire les en-têtes HTTP.

Le problème est que, lorsque je lance le test, je reçois une exception NotSupportedException:

Configuration non valide sur un membre non virtuel (remplaçable dans VB): x => x.WebSiteSettings

Voici le code moqueur pertinent:

 var mockWebSiteSettings = new Mock(); var mockController = new Mock(SomeRepository); mockController.Setup(x => x.WebSiteSettings).Returns(mockWebSiteSettings.Object); _controller = mockController.Object; var httpContextBase = MvcMockHelpers.FakeHttpContext(); httpContextBase.Setup(x => x.Request.ServerVariables).Returns(new NameValueCollection { {"HTTP_HOST","localhost.www.mydomain.com"}, }); _controller.SetFakeControllerContext(httpContextBase.Object); 

Si je rends la propriété WebsiteSettings virtuelle , le test réussit.

Mais je ne comprends pas pourquoi j’ai besoin de faire ça. En réalité, je ne remplace pas la propriété, je me moque simplement de la manière dont elle est configurée.

Est-ce que je manque quelque chose ou est-ce que je fais mal

Moq et d’autres infrastructures de moquage similaires ne peuvent simuler que des interfaces, des méthodes / propriétés abstraites (sur des classes abstraites) ou des méthodes / propriétés virtuelles sur des classes concrètes.

Cela est dû au fait qu’il génère un proxy qui implémentera l’interface ou créera une classe dérivée qui redéfinira ces méthodes pouvant être remplacées afin d’intercepter les appels.

J’ai créé une interface et une classe wrapper. par exemple

  public interface IWebClient { ssortingng DownloadSsortingng(ssortingng url); } public class WebClient : IWebClient { private readonly System.Net.WebClient _webClient = new System.Net.WebClient(); public ssortingng DownloadSsortingng(ssortingng url) { return _webClient.DownloadSsortingng(url); } } 

et dans vos tests unitaires, simulez simplement l’interface:

  var mockWebClient = new Mock(); 

Évidemment, vous devrez peut-être inclure plus de propriétés / méthodes. Mais fait le tour.

Une autre astuce utile pour d’autres problèmes moqueurs, tels que la modification de l’heure de la date actuelle (j’utilise toujours l’heure de l’heure UTC):

 public interface IDateTimeUtcNowProvider { DateTime UtcNow { get; } } public class DateTimeUtcNowProvider : IDateTimeUtcNowProvider { public DateTime UtcNow { get { return DateTime.UtcNow; } } } 

Par exemple, si vous avez un service qui s’exécute toutes les x minutes, vous pouvez simplement simuler IDateTimeProvider et renvoyer une heure ultérieure pour vérifier que le service a été exécuté de nouveau …

“Alors …. qu’est-ce que j’ai fait est le seul moyen?”

Non, pas le seul moyen – vous feriez bien mieux de mettre en place une interface et de vous en moquer. Ensuite, vos méthodes réelles peuvent être virtuelles ou non à votre guise.

Bien que tout ce qui a été dit auparavant soit vrai, il vaut la peine de savoir que l’approche de simulation de procuration (comme celle utilisée par moq) n’est pas la seule possible.

Consultez http://www.typemock.com/ pour une solution complète qui vous permet de vous moquer des deux classes scellées, des méthodes non virtuelles, etc. Assez puissant.