wake-up-neo.com

Warum muss die Eigenschaft, die ich verspotten möchte, virtuell sein?

Ich mache einige Unit-Tests und verspotte einige Eigenschaften mit Moq .

Nun ist dies ein Controller Test (ASP.NET MVC 3). Meine Controller stammen von einem abstract - Controller namens AbstractController .

Dieser Controller hat eine Abhängigkeit vom HTTP-Kontext (um Themen wie Theming, domänenspezifische, auf HTTP-Host-Headern basierende Logik usw.) auszuführen.

Dies erfolgt über eine Eigenschaft namens WebSiteSettings :

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

   // other code
}

Beachten Sie das private Set - der ctor richtet es ein. Also habe ich es geändert, um eine Schnittstelle zu verwenden, und das habe ich verspottet:

public IWebSiteSettings WebSiteSettings { get; private set; }

Ich habe dann ein "FakeWebSiteSettings" erstellt, das den HTTP-Kontext verspottet, damit er die HTTP-Header lesen kann.

Das Problem ist, wenn ich den Test starte, bekomme ich eine NotSupportedException:

Ungültiges Setup für ein nicht virtuelles Mitglied (in VB überschreibbar): x => x.WebSiteSettings

Hier ist der relevante Mocking-Code:

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);

Wenn ich die WebsiteSettings-Eigenschaft virtual - mache, ist der Test bestanden.

Aber ich kann nicht verstehen, warum ich das tun muss. Ich bin eigentlich nicht überschreibe die Eigenschaft, ich mache einfach nur Spott, wie sie eingerichtet wird. 

Vermisse ich etwas oder mache ich das falsch?

36
RPM1984

Moq und andere ähnliche Mocking-Frameworks können nur Schnittstellen, abstrakte Methoden/Eigenschaften (für abstrakte Klassen) oder virtuelle Methoden/Eigenschaften für konkrete Klassen simulieren.

Dies liegt daran, dass ein Proxy generiert wird, der die Schnittstelle implementiert oder eine abgeleitete Klasse erstellt, die diese überschreibbaren Methoden überschreibt, um Aufrufe abzufangen.

57
aqwert

Ich habe eine Interface- und Wrapper-Klasse erstellt. z.B.

    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);
        }
    }

und dann simulieren Sie in Ihren Unit-Tests einfach die Schnittstelle:

        var mockWebClient = new Mock<IWebClient>();

Natürlich müssen Sie möglicherweise weitere Eigenschaften/Methoden angeben. Aber macht den Trick.

Ein weiterer nützlicher Trick für andere Scheinprobleme, z. B. das Ändern der aktuellen Datumszeit (ich verwende immer die UTC-Datumszeit):

public interface IDateTimeUtcNowProvider
{
    DateTime UtcNow { get; } 
}

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

z.B. Wenn Sie einen Dienst haben, der alle x Minuten ausgeführt wird, können Sie einfach den IDateTimeProvider ausspucken und eine Zeit zurückgeben, die später zum Überprüfen des Dienstes verwendet wird.

4
mkaj

"Also ... was ich getan habe, ist der einzige Weg?"

Nein, nicht der einzige Weg - Sie sind viel besser dran, eine Schnittstelle zu implementieren und das zu verspotten. Dann können Ihre tatsächlichen Methoden virtuell sein oder nicht, wie Sie möchten.

3
Giles Bradshaw

Obwohl alles, was zuvor gesagt wurde, wahr ist, lohnt es sich zu wissen, dass der Proxy-Mocking-Ansatz (wie derjenige, den moq verwendet) nicht der einzig mögliche ist. 

Überprüfen Sie http://www.typemock.com/ , um eine umfassende Lösung zu erhalten, mit der Sie sowohl versiegelte Klassen als auch nicht virtuelle Methoden simulieren können. Ziemlich leistungsstark.

0
mikus