Bir uzantı yöntemiyle alay etmek için Moq'u nasıl kullanabilirim?


87

Bir uzatma yönteminin sonuçlarına bağlı bir test yazıyorum, ancak bu uzatma yönteminin gelecekte bu testi kırmasını istemiyorum. Bu sonucu alay etmek bariz bir seçim gibi görünüyordu, ancak Moq statik bir yöntemi geçersiz kılmak için bir yol sunmuyor gibi görünüyor (bir uzatma yöntemi için bir gereklilik). Moq.Protected ve Moq.Stub ile benzer bir fikir var, ancak bu senaryo için hiçbir şey sunmuyorlar. Bir şey mi özlüyorum yoksa bunu farklı bir şekilde mi yapmalıyım?

İşte olağan "geçersiz kılınamayan bir üye için geçersiz beklenti" ile başarısız olan önemsiz bir örnek . Bu, bir uzatma yöntemiyle dalga geçmeye ihtiyaç duymanın kötü bir örneğidir, ancak yapmalıdır.

public class SomeType {
    int Id { get; set; }
}

var ListMock = new Mock<List<SomeType>>();
ListMock.Expect(l => l.FirstOrDefault(st => st.Id == 5))
        .Returns(new SomeType { Id = 5 });

Bunun yerine Isolator kullanmamı önerebilecek herhangi bir TypeMock bağımlısına gelince: TypeMock işi gözü kapalı ve sarhoş bir şekilde yapabileceği için gösterilen çabayı takdir ediyorum, ancak bütçemiz yakın zamanda artmıyor.


3
Bir kopyası burada bulunabilir: stackoverflow.com/questions/2295960/… .
Oliver

6
Bu soru, ondan tam bir yıl daha eski. Çoğaltma varsa, diğer tarafa gider.
patridge

2
2019'da hala uygun bir çözüm yok!
TanvirArjel

1
@TanvirArjel Aslında, uzun yıllardan beri JustMock'u uzatma yöntemleriyle alay etmek için kullanabilirsiniz . Ve başka herhangi bir yöntemle alay etmek kadar basit. İşte dokümantasyona bir bağlantı: Extension Methods Mocking
Mihail Vladov

Yanıtlar:


71

Uzatma yöntemleri, gizlenmiş statik yöntemlerdir. Moq veya Rhinomocks gibi alaycı çerçeveler yalnızca nesnelerin sahte örneklerini oluşturabilir, bu, statik yöntemlerle alay etmenin mümkün olmadığı anlamına gelir.


69
@ Mendelt..Öyleyse bir birim dahili olarak bir genişletme yöntemi olan bir yöntemi nasıl test eder? olası alternatifler nelerdir?
Sai Avinash

1
@Alexander Aynı soruyu sormuştum ve sonra bu soruyu fevkalade cevapladı: agooddayforscience.blogspot.com/2017/08/… -Şuna bir bakın, bu bir hayat kurtarıcı!
letie

31

Uzantı yöntemleri kodunu değiştirebiliyorsanız, test edebilmek için şu şekilde kodlayabilirsiniz:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

public static class MyExtensions
{
    public static IMyImplementation Implementation = new MyImplementation();

    public static string MyMethod(this object obj)
    {
        return Implementation.MyMethod(obj);
    }
}

public interface IMyImplementation
{
    string MyMethod(object obj);
}

public class MyImplementation : IMyImplementation
{
    public string MyMethod(object obj)
    {
        return "Hello World!";
    }
}

Dolayısıyla, genişletme yöntemleri yalnızca uygulama arabiriminin etrafındaki bir sarmalayıcıdır.

(Yalnızca uygulama sınıfını, bir tür sözdizimsel şeker olan uzantı yöntemleri olmadan kullanabilirsiniz.)

Ve uygulama arayüzüyle alay edebilir ve bunu, extensions sınıfı için uygulama olarak ayarlayabilirsiniz.

public class MyClassUsingExtensions
{
    public string ReturnStringForObject(object obj)
    {
        return obj.MyMethod();
    }
}

[TestClass]
public class MyTests
{
    [TestMethod]
    public void MyTest()
    {
        // Given:
        //-------
        var mockMyImplementation = new Mock<IMyImplementation>();

        MyExtensions.Implementation = mockMyImplementation.Object;

        var myClassUsingExtensions = new MyClassUsingExtensions();

        // When:
        //-------
        var myObject = new Object();
        myClassUsingExtensions.ReturnStringForObject(myObject);

        //Then:
        //-------
        // This would fail because you cannot test for the extension method
        //mockMyImplementation.Verify(m => m.MyMethod());

        // This is success because you test for the mocked implementation interface
        mockMyImplementation.Verify(m => m.MyMethod(myObject));
    }
}


15

Alay etmem gereken uzantı yöntemleri için bir sarmalayıcı sınıfı oluşturdum.

public static class MyExtensions
{
    public static string MyExtension<T>(this T obj)
    {
        return "Hello World!";
    }
}

public interface IExtensionMethodsWrapper
{
    string MyExtension<T>(T myObj);
}

public class ExtensionMethodsWrapper : IExtensionMethodsWrapper
{
    public string MyExtension<T>(T myObj)
    {
        return myObj.MyExtension();
    }
}

Daha sonra, IOC kabınız ile testlerinizde ve kodunuzdaki sarmalayıcı yöntemleriyle alay edebilirsiniz.


Bu, artık kodunuzda uzantı yöntemleri sözdizimini kullanamayacağınız bir geçici çözümdür. Ancak, uzantı yöntemleri sınıfını değiştiremediğinizde yardımcı olur.
informatorius

@informatorius Bununla ne demek istiyorsun? Kodunuzda MyExtensions sınıfından MyExtension () kullanırsınız. Testlerinizde, parametreyi sağlayan ExtensionMethodsWrapper () sınıfından MyExtension () kullanırsınız.
ranthonissen

Test edilen sınıfım MyExtensions'tan MyExtension () kullanıyorsa, MyExtensions ile alay edemem. Bu nedenle test edilen sınıf, alay edilebilmesi için IExtensionMethodsWrapper'ı kullanmalıdır. Ancak test edilen sınıf artık uzantı yöntemi sözdizimini kullanamaz.
bilgi sahibi

1
OP'nin tüm amacı budur. Bu, bunun için bir geçici çözümdür.
ranthonissen

4

Uzatma yöntemleri için normalde aşağıdaki yaklaşımı kullanırım:

public static class MyExtensions
{
    public static Func<int,int, int> _doSumm = (x, y) => x + y;

    public static int Summ(this int x, int y)
    {
        return _doSumm(x, y);
    }
}

_DoSumm'u oldukça kolay bir şekilde enjekte etmeyi sağlar.


2
Bunu yeni denedim, ancak uzantının başka bir derlemede bulunan başka bir yönteme geçirirken kullandığı Parent alay nesnesini geçerken sorunlar var. Bu durumda, bu teknikle bile orijinal uzantı, yöntem diğer derlemede çağrıldığında, bir tür kapsam belirleme sorunu olarak seçilir. Doğrudan Birim Testi projesini ararsam sorun değil, ancak uzantı yöntemini çağıran başka bir kodu test ederken bu yardımcı olmuyor.
Stephen York

@Stephen Bundan nasıl kaçınılacağından emin değilim. Hiç bu tür bir kapsam sorunu
yaşamadım

0

Yapabileceğiniz en iyi şey, uzantı yöntemine sahip tür için özel bir uygulama sağlamaktır, örneğin:

[Fact]
public class Tests
{
    public void ShouldRunOk()
    {
        var service = new MyService(new FakeWebHostEnvironment());

        // Service.DoStuff() internally calls the SomeExtensionFunction() on IWebHostEnvironment
        // Here it works just fine as we provide a custom implementation of that interface
        service.DoStuff().Should().NotBeNull();
    }
}

public class FakeWebHostEnvironment : IWebHostEnvironment
{
    /* IWebHostEnvironment implementation */

    public bool SomeExtensionFunction()
    {
        return false;
    }
}
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.