Moq ile Alay Genişletme Yöntemleri


175

Önceden var olan bir Arayüzüm var ...

public interface ISomeInterface
{
    void SomeMethod();
}

ve ben bu arayüzü bir mixin kullanarak uzattım ...

public static class SomeInterfaceExtensions
{
    public static void AnotherMethod(this ISomeInterface someInterface)
    {
        // Implementation here
    }
}

Bunu denemek istediğim bir sınıf var ...

public class Caller
{
    private readonly ISomeInterface someInterface;

    public Caller(ISomeInterface someInterface)
    {
        this.someInterface = someInterface;
    }

    public void Main()
    {
        someInterface.AnotherMethod();
    }
}

ve arayüzle alay etmek ve uzantı yöntemine yapılan çağrıyı doğrulamak istediğim bir test ...

    [Test]
    public void Main_BasicCall_CallsAnotherMethod()
    {
        // Arrange
        var someInterfaceMock = new Mock<ISomeInterface>();
        someInterfaceMock.Setup(x => x.AnotherMethod()).Verifiable();

        var caller = new Caller(someInterfaceMock.Object);

        // Act
        caller.Main();

        // Assert
        someInterfaceMock.Verify();
    }

Ancak bu testi çalıştırmak bir istisna oluşturur ...

System.ArgumentException: Invalid setup on a non-member method:
x => x.AnotherMethod()

Benim sorum, mixin çağrısını alay etmenin güzel bir yolu var mı?


3
Deneyimlerime göre, mixin ve uzatma yöntemleri terimleri ayrı şeylerdir. Bu durumda ikincisini
karışıklıklardan

Yanıtlar:


33

Alaycı çerçeve ile "doğrudan" alay statik yöntemi (dolayısıyla uzantısı yöntemi). Microsoft'un farklı bir yaklaşım uygulayan ücretsiz bir aracı olan Moles'i ( http://research.microsoft.com/en-us/projects/pex/downloads.aspx ) deneyebilirsiniz . İşte aracın açıklaması:

Moles, .NET'te delegelere dayanan test saplamaları ve sapmalar için hafif bir çerçevedir.

Moller, kapalı tiplerde sanal olmayan / statik yöntemler de dahil olmak üzere herhangi bir .NET yöntemini saptırmak için kullanılabilir.

Molleri herhangi bir test çerçevesiyle kullanabilirsiniz (bu konuda bağımsızdır).


2
Moles dışında, nesneleri taklit etmek için .NET'in profiler API'sini kullanan ve böylece herhangi bir çağrının yerini alabilecek diğer (ücretsiz olmayan) alaycı çerçeveler vardır. Bildiğim ikisi Telerik'in JustMock ve TypeMock İzolatörü .
Marcel Gosselin

6
Teorik olarak moller iyidir, ancak denediğimde beni kullanarak durduran üç sorun buldum ... 1) Resharper NUnit koşucusunda çalışmaz 2) Her stubbed montajı için manuel olarak bir köstebek meclisi oluşturmanız gerekir 3 ) Bir inatçı yöntem değiştiğinde bir köstebek montajını manuel olarak yeniden oluşturmanız gerekir.
Russell Giddings

26

Bu sorunu çözmek için bir Wrapper kullandım. Bir sarıcı nesnesi oluşturun ve alaycı yönteminizi iletin.

Paul Irwin'in Birim Testi için Alay Statik Yöntemleri'ne bakın , güzel örnekleri var.


12
Bu cevabı seviyorum çünkü söylediği şey (doğrudan söylemeden) test edilebilir yapmak için kodunuzu değiştirmeniz gerekiyor. İşte böyle çalışır. Mikroçip / IC / ASIC tasarımında, bu çiplerin sadece çalışmak için tasarlanmakla kalmayıp aynı zamanda test edilebilir olmak için daha da tasarlanması gerektiğini anlayın, çünkü bir mikroçip test edemezseniz, işe yaramaz - garanti edemezsiniz iş. Aynı şey yazılım için de geçerli. Test edilebilir olarak inşa etmediyseniz, bu ... işe yaramaz. Test edilebilir olarak oluşturun, bu bazı durumlarda kodu yeniden yazmak (ve sarmalayıcılar kullanmak) anlamına gelir ve ardından test eden otomatik testleri oluşturun.
Michael Plautz

2
Dapper, Dapper.Contrib ve IDbConnection'ı saran küçük bir kütüphane oluşturdum. Kopenhag
Drew

15

Girdi için alay etmeye çalıştığım uzatma yönteminin içini bulmak ve uzantının içinde olup bitenleri alay etmek zorunda olduğumu keşfettim.

Bir uzantıyı doğrudan yönteminize kod ekleyerek kullandım. Bu, uzantının kendisinden ziyade uzantının içinde neler olduğunu taklit etmem gerektiği anlamına geliyordu.


11

Gerçek olandan devralınan ve uzantı yöntemiyle aynı imzalı bir üyesi olan bir test arabirimini alay edebilirsiniz.

Daha sonra test arabirimini alay edebilir, gerçek olanı sahte ekleyebilir ve kurulumda test yöntemini çağırabilirsiniz.

Daha sonra sahte uygulamanız istediğiniz yöntemi çağırabilir veya yöntemin çağrıldığını kontrol edebilir:

IReal //on which some extension method is defined
{
    ... SomeRegularMethod(...);
}

static ExtensionsForIReal
{
    static ... SomeExtensionMethod(this IReal iReal,...);
}

ITest: IReal
{
    //This is a regular method with same name and signature as the extension without the "this IReal iReal" parameter
    ... SomeExtensionMethod(...);
}

var someMock = new Mock<ITest>();
Mock.As<IReal>(); //ad IReal to the mock
someMock.Setup(x => x.SomeExtensionMethod(...)).Verifiable(); //Calls SomeExtensionMethod on ITest
someMock.As<IReal>().Setup(x => x.SomeRegularMethod(...)).Verifiable(); //Calls SomeRegularMethod on IReal

Bu arayüzde bulunan Håvard S çözümü sayesinde iki arayüzü destekleyen bir alay nasıl uygulanacağı. Bir kez buldum, test arayüzü ve statik yöntem ile adapte bir kek yürüyüş oldu.


Bize için aynı numune imza memnun gösterebilir SomeNotAnExtensionMethodve SomeNotAnExtensionMethod? Şimdi bir arayüz içinde bir uzatma yöntemi imza oluşturmak için nasıl bir fikrim var ...
Peter Csala

1
@Peter Csala: Bu net değilse özür dilerim. Gönderiyi daha net hale getirmek için güncelledim ve SomeNotAnExtensionMethod öğesini SomeRegularMethod olarak yeniden adlandırdım.
pasx

Nasıl geçmek yapmak iRealiçin parametreyi SomeExtensionMethoddurumunda ITest? İlk parametre olarak iletirseniz, nasıl kurulur? Setup( x=> x.SomeExtensionMethod(x, ...)bu bir çalışma zamanı istisnasına neden olur.
Peter Csala

Sen geçemezsin. Sahte bakış açısından ITest, bu parametre olmadan kodunuzdaki uzantı yöntemine yapılan çağrıların imzasını eşleştirmek için düzenli bir yöntem içerir, böylece burada hiçbir zaman bir IReal almazsınız ve her durumda IReal / ITest uygulaması sahte olur . Eğer taklit IReal bazı özelliklerine SomeExtensionMethod erişmek istiyorsanız, sahte tüm yapmalısınız örneğin: object _mockCache = whatever... `Kurulum (x => x.SomeExtensionMethod (...) .. Geri arama (() => yapabilirsiniz erişim _mockCache here);)
pasx

8

Bir uzantı yöntemini JustMock ile kolayca taklit edebilirsiniz . API, normal alay yöntemiyle aynıdır. Aşağıdakileri göz önünde bulundur

public static string Echo(this Foo foo, string strValue) 
{ 
    return strValue; 
}

Bu yöntemi düzenlemek ve doğrulamak için aşağıdakileri kullanın:

string expected = "World";

var foo = new Foo();
Mock.Arrange(() => foo.Echo(Arg.IsAny<string>())).Returns(expected);

string result = foo.Echo("Hello");

Assert.AreEqual(expected, result);

Ayrıca belgelere bir bağlantı: Uzantı Yöntemleri Alaycı


2

Nesnenin kendisini sararken sarmalayıcıyı (adaptör deseni) kullanmayı seviyorum. Nesnenin bir parçası olmayan bir uzantı yöntemi sarma için kullanacağımdan emin değilim.

Action, Func, Predicate veya delegate türlerinde dahili bir Lazy Enjekte Edilebilir Özellik kullanıyorum ve birim testi sırasında yöntemin enjekte edilmesine (değiştirilmesine) izin veriyorum.

    internal Func<IMyObject, string, object> DoWorkMethod
    {
        [ExcludeFromCodeCoverage]
        get { return _DoWorkMethod ?? (_DoWorkMethod = (obj, val) => { return obj.DoWork(val); }); }
        set { _DoWorkMethod = value; }
    } private Func<IMyObject, string, object> _DoWorkMethod;

Sonra gerçek yöntem yerine Func çağırırsınız.

    public object SomeFunction()
    {
        var val = "doesn't matter for this example";
        return DoWorkMethod.Invoke(MyObjectProperty, val);
    }

Daha eksiksiz bir örnek için http://www.rhyous.com/2016/08/11/unit-testing-calls-to-complex-extension-methods/ adresine bakın.


Bu iyi, ancak okuyucular _DoWorkMethod'un sınıfın her örneğinin artık bir alan daha ayırması gereken sınıfın yeni bir alanı olduğunu bilmelidir. Bunun önemli olması nadirdir, ancak bazen herhangi bir zamanda ayırdığınız örnek sayısına bağlı olarak değişir. _DoWorkMethod statik yaparak bu soruna geçici bir çözüm bulabilirsiniz. Bunun dezavantajı, aynı anda çalışan birim testleriniz varsa, aynı statik değeri değiştirebilecek iki farklı birim testinin tamamlanmasıdır.
zumalifeguard

-1

Bu nedenle, Moq kullanıyorsanız ve bir Extension yönteminin sonucunu alay etmek istiyorsanız, o zaman SetupReturnsDefault<ReturnTypeOfExtensionMethod>(new ConcreteInstanceToReturn())alay etmeye çalıştığınız uzantı yöntemine sahip olan sahte sınıf örneğinde kullanabilirsiniz .

Mükemmel değil, ancak birim test amaçları için iyi çalışıyor.


Sanırım SetReturnsDefault <T> ()
David

bu asla somut örneği döndürmez. Özel bir c # sınıfı olması durumunda null!
HelloWorld
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.