Mengapa saya mendapatkan Pengecualian dengan pesan "Pengaturan tidak valid pada anggota non-virtual (dapat ditimpa dalam VB) ..."?

176

Saya memiliki tes unit di mana saya harus mengejek metode non-virtual yang mengembalikan tipe bool

public class XmlCupboardAccess
{
    public bool IsDataEntityInXmlCupboard(string dataId,
                                          out string nameInCupboard,
                                          out string refTypeInCupboard,
                                          string nameTemplate = null)
    {
        return IsDataEntityInXmlCupboard(_theDb, dataId, out nameInCupboard, out refTypeInCupboard, nameTemplate);
    }
}

Jadi saya punya objek tiruan dari XmlCupboardAccesskelas dan saya mencoba untuk mengatur tiruan untuk metode ini dalam test case saya seperti yang ditunjukkan di bawah ini

[TestMethod]
Public void Test()
{
    private string temp1;
    private string temp2;
    private Mock<XmlCupboardAccess> _xmlCupboardAccess = new Mock<XmlCupboardAccess>();
    _xmlCupboardAccess.Setup(x => x.IsDataEntityInXmlCupboard(It.IsAny<string>(), out temp1, out temp2, It.IsAny<string>())).Returns(false); 
    //exception is thrown by this line of code
}

Tapi baris ini melempar pengecualian

Invalid setup on a non-virtual (overridable in VB) member: 
x => x.IsDataEntityInXmlCupboard(It.IsAny<String>(), .temp1, .temp2, 
It.IsAny<String>())

Ada saran bagaimana menyiasati pengecualian ini?

Rahul Lodha
sumber
Tergantung pada tes Anda XmlCupboardAccess?
Preston Guillot
9
sederhana .. Anda harus menandainya virtual. Moq tidak bisa mengejek tipe beton yang tidak bisa ditimpa.
Simon Whitehead

Jawaban:

265

Moq tidak bisa mengejek metode non-virtual dan kelas disegel. Saat menjalankan tes menggunakan objek tiruan, MOQ sebenarnya membuat jenis proxy di dalam memori yang mewarisi dari "XmlCupboardAccess" Anda dan mengabaikan perilaku yang telah Anda atur dalam metode "SetUp". Dan seperti yang Anda tahu di C #, Anda dapat menimpa sesuatu hanya jika ditandai sebagai virtual yang tidak terjadi pada Java. Java menganggap setiap metode non-statis adalah virtual secara default.

Hal lain yang saya percaya harus Anda pertimbangkan adalah memperkenalkan antarmuka untuk "CupboardAccess" Anda dan mulai mengejek antarmuka. Ini akan membantu Anda memisahkan kode dan memiliki manfaat dalam jangka panjang.

Terakhir, ada kerangka kerja seperti: TypeMock dan JustMock yang bekerja langsung dengan IL dan karenanya dapat mengejek metode non-virtual. Namun keduanya, merupakan produk komersial.

Amol
sumber
59
Memberi +1 pada fakta bahwa Anda seharusnya hanya mengejek antarmuka. Pertanyaan ini memecahkan apa yang saya temui, karena saya tidak sengaja mengejek kelas dan bukan antarmuka yang mendasarinya.
Paul Raff
1
Ini tidak hanya menyelesaikan masalah tetapi juga merupakan praktik yang baik untuk menggunakan antarmuka untuk semua kelas Anda yang perlu pengujian. Moq pada dasarnya memaksa Anda untuk memiliki Inversi Ketergantungan yang baik di mana karena beberapa kerangka kerja mengejek lainnya memungkinkan Anda untuk mengatasi prinsip ini.
Xipooo
Apakah ini dianggap pelanggaran prinsip ini jika saya memiliki implementasi palsu, misalnya, FakePeopleRepository, dari antarmuka saya, misalnya, IPeopleRepository, dan saya mengejek implementasi palsu? Saya pikir IoC masih dipertahankan karena dalam pengaturan pengujian saya, saya harus meneruskan objek palsu ke kelas layanan saya yang mengambil antarmuka dalam konstruktornya.
paz
1
@paz Inti dari menggunakan MOQ adalah untuk menghindari implementasi palsu. Sekarang pertimbangkan berapa banyak varian dari implementasi palsu yang Anda perlu memeriksa kondisi batas dll. Secara teori, ya, Anda bisa mengejek implementasi palsu. Tapi praktis itu terdengar seperti bau kode.
Amol
Perhatikan bahwa kesalahan ini sebenarnya dapat terjadi dengan metode ekstensi pada antarmuka, yang mungkin membingungkan.
Dan Pantry
34

Sebagai bantuan kepada siapa saja yang memiliki masalah yang sama dengan saya, saya tidak sengaja salah ketik jenis implementasi, bukan antarmuka misalnya

var mockFileBrowser = new Mock<FileBrowser>();

dari pada

var mockFileBrowser = new Mock<IFileBrowser>();
Ralt
sumber
5

Silakan lihat Mengapa properti yang ingin saya tiru perlu virtual?

Anda mungkin harus menulis antarmuka pembungkus atau menandai properti sebagai virtual / abstrak karena Moq membuat kelas proxy yang digunakannya untuk mencegat panggilan dan mengembalikan nilai kustom yang Anda masukkan ke dalam .Returns(x)panggilan.

Bryida
sumber
5

Alih-alih mengejek kelas beton Anda harus mengejek antarmuka kelas itu. Ekstrak antarmuka dari kelas XmlCupboardAccess

public interface IXmlCupboardAccess
{
    bool IsDataEntityInXmlCupboard(string dataId, out string nameInCupboard, out string refTypeInCupboard, string nameTemplate = null);
}

Dan bukannya

private Mock<XmlCupboardAccess> _xmlCupboardAccess = new Mock<XmlCupboardAccess>();

mengubah

private Mock<IXmlCupboardAccess> _xmlCupboardAccess = new Mock<IXmlCupboardAccess>();
Sashus
sumber
3

Anda juga akan mendapatkan kesalahan ini jika Anda memverifikasi bahwa metode ekstensi suatu antarmuka dipanggil.

Misalnya jika Anda mengejek:

var mockValidator = new Mock<IValidator<Foo>>();
mockValidator
  .Verify(validator => validator.ValidateAndThrow(foo, null));

Anda akan mendapatkan pengecualian yang sama karena .ValidateAndThrow()merupakan ekstensi pada IValidator<T>antarmuka.

public static void ValidateAndThrow<T>(this IValidator<T> validator, T instance, string ruleSet = null)...

Scotty.NET
sumber
-12

Kode:

private static void RegisterServices(IKernel kernel)
{
    Mock<IProductRepository> mock=new Mock<IProductRepository>();
    mock.Setup(x => x.Products).Returns(new List<Product>
    {
        new Product {Name = "Football", Price = 23},
        new Product {Name = "Surf board", Price = 179},
        new Product {Name = "Running shose", Price = 95}
    });

    kernel.Bind<IProductRepository>().ToConstant(mock.Object);
}        

tapi lihat pengecualian.

Borat
sumber
4
Bisakah Anda memberikan penjelasan tentang solusi Anda? Juga, "lihat pengecualian ..." dibiarkan menggantung. Dapatkah Anda memperluas ini?
amadan