web-dev-qa-db-fra.com

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 en utilisant Moq.

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

Ce contrôleur a une dépendance sur le contexte HTTP (afin de faire des choses comme la thématisation, la logique spécifique au domaine basée sur les en-têtes d’hôte HTTP, 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 un 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<FakeWebSiteSettings>();
var mockController = new Mock<MyController>(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 fais la propriété WebsiteSettingsvirtual _ - le test réussit.

Mais je ne comprends pas pourquoi j'ai besoin de faire ça. En fait, je ne suis pas surchargeant la propriété, je me moque simplement de la façon dont elle est configurée. 

Est-ce que je manque quelque chose ou est-ce que je me trompe?

36
RPM1984

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

En effet, il génère un proxy qui implémentera l'interface ou créera une classe dérivée qui remplacera ces méthodes pouvant être remplacées afin d'intercepter les appels.

57
aqwert

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

    public interface IWebClient
    {
        string DownloadString(string url);
    }

    public class WebClient : IWebClient
    {
        private readonly System.Net.WebClient _webClient = new System.Net.WebClient();

        public string DownloadString(string url)
        {
            return _webClient.DownloadString(url);
        }
    }

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

        var mockWebClient = new Mock<IWebClient>();

É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 disposez d'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é réexécuté ...

4
mkaj

"Alors .... qu'est-ce que j'ai fait est le seul moyen?"

Non, pas le seul moyen - vous feriez 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.

3
Giles Bradshaw

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. 

Vérifiez 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. Très puissant.

0
mikus