Cum bateți o clasă închisă?

Mocking sealed classes can be quite a pain. I currently favor an Adapter pattern to handle this, but something about just keeps feels weird.

Deci, care este cel mai bun mod de a falsifica clase sigilate?

Java answers are more than welcome. In fact, I would anticipate that the Java community has been dealing with this longer and has a great deal to offer.

Dar aici sunt câteva dintre opiniile .NET:

0
fr hi bn
adăugat autor cregox, sursa

10 răspunsuri

Există o modalitate de a implementa o clasă sigilată de la o interfață ... și să machetezi interfața în schimb?

Ceva in mine simte ca, in primul rand, clasele sigilate sunt grele, dar asta e doar eu :)

0
adăugat
Cred că clasele închise sunt minunate ... problema este de testare. Acesta miroase că trebuie să ne schimbăm întregul design din cauza limitărilor în proiectele .NET. În multe cazuri D.I. este pur și simplu o modalitate de a înțelege faptul că clasele / metodele statice sunt greu de testat.
adăugat autor Beep beep, sursa

Regula mea generală este că obiectele pe care trebuie să le bat joc ar trebui să aibă și o interfață comună. Cred că acest lucru este potrivit pentru design-înțelept și face testele mult mai ușor (și de obicei este ceea ce obțineți dacă faceți TDD). Mai multe despre acest lucru pot fi citite în Blogul de testare Google ultimul post (Vezi punctul 9).

De asemenea, am lucrat în principal în Java în ultimii 4 ani și pot spune că pot conta, pe de o parte, de câte ori am creat o clasă finală (sigilată). O altă regulă aici este că ar trebui să am întotdeauna un motiv bun pentru a sigila o clasă, spre deosebire de sigilarea ei în mod implicit.

0
adăugat
@ Tyler lucrează aici
adăugat autor abyx, sursa
@BryanWatts Nu sunt de acord cu asta. Sechestrarea unei clase îi împiedică pe coderi să moștenească clasa. S-ar putea să-l extindeți util într-un mod pe care nu l-ați prevăzut. Nu trebuie să vă faceți griji că le-ați înșelat ceva; (probabil) nu este responsabilitatea ta. Aș elimina de fapt cuvântul cheie sigilat .
adăugat autor Jez, sursa
Aș spune că ar trebui să aveți un motiv bun să nu sigiliți o clasă. Lăsând o clasă deschisă înseamnă că trebuie să vă gândiți cum va fi folosită de către moștenitori, ceea ce va deschide o sursă de decizii cu privire la întregul cod din clasă (virtuală, proprietăți protejate sau variabile de membru privat etc.) Este greu pentru a proiecta corect o clasă pentru moștenire. Nu ar trebui să puteți prelungi o clasă doar din motive de extensie; derivarea ar trebui să însemne ceva specific problemelor modelate. În caz contrar, preferați compoziția.
adăugat autor Bryan Watts, sursa
@Jez: Dacă nu aș fi considerat extensie la scrierea clasei, ce motiv este să ai încredere în ea?
adăugat autor Bryan Watts, sursa
Bryan - așa cum ai bate joc de o clasă sigilată atunci? Sunt de acord cu tine din punct de vedere teoretic, dar dacă scrii ceva dependent de clasa sigilată, atunci testele tale depind și de clasa sigilată.
adăugat autor Beep beep, sursa
@abyx: Din păcate, nu este întotdeauna alegerea dezvoltatorului dacă o clasă este sigilată sau nu. Luați, de exemplu, System.Web.HttpServerUtility în ASP.NET ...
adăugat autor chiccodoro, sursa
link-ul este mort
adăugat autor DevDave, sursa

Deși în prezent este disponibil numai în versiunea beta, cred că merită să ținem cont de shim caracteristica noului Fake framework (parte a versiunea Visual Studio 11 versiunea Beta).

Tipurile Shim oferă un mecanism pentru a ocoli orice metodă .NET unui delegat definit de utilizator. Tipurile de tip Shim sunt generate de codul generator al Fake-urilor și folosesc delegați, pe care îi numim tipuri de shim, pentru a specifica noile implementări ale metodei. Sub capotă, tipurile de șabloane utilizează callback-uri care au fost injectate la runtime în corpurile MSIL de metodă.

Personal, mă uitam la folosirea acestei metode pentru a bate metodele pe clase de clase închise, cum ar fi DrawingContext.

0
adăugat

Cred că Moles , de la Microsoft Research, vă permite să faceți acest lucru. Din pagina Moles:

Moolele pot fi folosite pentru a ocoli orice .NET   inclusiv non-virtuale / statice   metode în tipuri sigilate.

UPDATE: there is a new framework called "Fakes" in the upcoming VS 11 release that is designed to replace Moles:

The Fakes Framework in Visual Studio 11 is the next generation of Moles & Stubs, and will eventually replace it. Fakes is different from Moles, however, so moving from Moles to Fakes will require some modifications to your code. A guide for this migration will be available at a later date.

Requirements: Visual Studio 11 Ultimate, .NET 4.5

0
adăugat

Aproape întotdeauna evit dependențele de clasele externe adânc în codul meu. În schimb, aș prefera să folosesc un adaptor / pod pentru a vorbi cu ei. În acest fel, am de-a face cu semantica mea, iar durerea traducerii este izolată într-o singură clasă.

De asemenea, facilitează mișcarea dependențelor pe termen lung.

0
adăugat

Problema cu TypeMock este că scuză designul rău. Acum, știu că este deseori un design rău al altcuiva pe care îl ascundeți, dar lăsându-l în procesul de dezvoltare poate duce foarte ușor la designul rău.

Cred că dacă folosești un cadru de batjocură, ar trebui să folosești un stil tradițional (ca Moq) și să creezi un strat de izolare în jurul lucrurilor care nu pot fi deplasate și să învingi stratul de izolare.

0
adăugat
Sunt de acord cu Brad aici. Crearea unui obiect fals presupune că testați comportamentul public al acelui tip. Adică, spuneți în mod expres: "Am nevoie de API-ul public al acestui obiect să se comporte într-un anumit fel". Acest comportament este independent cu privire la implementarea API. Aceasta înseamnă că aveți o abstractizare deja definită (deși implicită) care trebuie să se comporte într-un anumit mod. Într-un sistem de tip static, acest lucru trebuie făcut explicit prin crearea unui tip abstract.
adăugat autor Sam Pearson, sursa
Nu interceptarea cu palme a tuturor aspectelor din vedere doar pentru că aveți nevoie de ele din cauza deficiențelor instrumentelor dvs. de testare nu este un design rău. De fapt, dimpotrivă, este vorba despre un design sane care se referă la design, nu la conformarea cu instrumentele dvs. Jur, uneori cred că TDD, așa cum este adesea practicat dogmatic, ar trebui să se numească într-adevăr "design-driven". De asemenea, vedeți web
Pavel - aveți și alt instrument decât TypeMock care poate oferi acest tip de testare? Testarea claselor construite cu modele sane (de exemplu, folosind metode statice, apeluri noi în cod, interfețe care limitează unde sunt necesare mai multe implementări etc.) sunt de multe ori aproape imposibil de testat.
adăugat autor Beep beep, sursa

Pentru .NET, puteți utiliza ceva de genul TypeMock , care utilizează API-ul de profil și vă permite să vă conectați la apeluri aproape orice.

0
adăugat
Unii oameni ar putea să nu plătească 80 de dolari pe lună pentru un cadru de testare.
adăugat autor Matt Grande, sursa
Cred că Moles vă permite să lucrați cu clase sigilate - și dacă aveți un abonament MSDN, este gratuit.
adăugat autor Mathias, sursa
+1. Utilizați instrumentele potrivite. Nu lăsați instrumentele să vă dicteze cum trebuie să faceți lucrurile. API-urile sunt pentru oameni, nu pentru instrumente - proiectați-le ca atare. Utilizați DI și interfețe și decuplarea completă în cazul în care are sens, nu doar pentru că aveți nevoie de ea pentru instrumente de testare.
adăugat autor Pavel Minaev, sursa
AFAIK una dintre principalele probleme cu aceste cadre, care pot falsifica clase sigilate, statice, etc. este performanță. Personal prefer să creez o interfață și să o folosesc în codul meu de producție, cu un obiect proxy de moștenire injectat în timpul producției și o injectare falsă în timpul testării.
adăugat autor Adam Ralph, sursa
O alternativă este Telerik JustMock. Puteți utiliza ediția comercială pentru a testa codul vechi, apoi treceți la ediția gratuită după ce au fost implementate interfețele și injecția de dependență.
adăugat autor kodefuguru, sursa
Pavel - problema este că modul "Right" în .NET vă conduce de obicei pe o cale de folosire "TypeMock to test". Urmând o cale în care există doar un singur instrument de testare disponibil, nu pare prea bun.
adăugat autor Beep beep, sursa

În general, iau calea de a crea o interfață și o clasă de adaptor / proxy pentru a facilita batjocurarea tipului sigilat. Cu toate acestea, am experimentat, de asemenea, să sărind crearea interfeței și efectuarea tipului de proxy non-sigilat cu metode virtuale. Acest lucru a funcționat bine atunci când proxy-ul este într-adevăr o clasă de bază naturală care încapsulează și utilizează o parte din clasa sigilată.

În cazul în care se ocupă de codul care a necesitat această adaptare, m-am săturat să realizez aceleași acțiuni pentru a crea interfața și tipul de proxy, astfel că am implementat o bibliotecă pentru a automatiza sarcina.

Codul este oarecum mai sofisticat decât eșantionul dat în articolul pe care îl referi, deoarece produce un ansamblu (în loc de cod sursă), permite generarea de coduri pentru orice tip și nu necesită o configurație mult mai mare.

For more information, please refer to this page.

0
adăugat

Este perfect rezonabil să bateți o clasă sigilată deoarece multe clase cadru sunt sigilate.

În cazul meu, încerc să mă bat joc de clasa MessageQueue .Net, astfel încât să pot TDD logica meticuloasă de excepție.

Dacă cineva are idei despre cum să depășească eroarea lui Moq cu privire la "Configurarea nevalidă pe un membru care nu este supradimensionat", vă rog să-mi spuneți.

cod:

    [TestMethod]
    public void Test()
    {
        Queue messages = new Queue();
        Action sendDelegate = msg => messages.Enqueue(msg);
        Func receiveDelegate =
            (v1, v2) =>
            {
                throw new Exception("Test Exception to simulate a failed queue read.");
            };

        MessageQueue mockQueue = QueueMonitorHelper.MockQueue(sendDelegate, receiveDelegate).Object;
    }
    public static Mock MockQueue
                (Action sendDelegate, Func receiveDelegate)
    {
        Mock mockQueue = new Mock(MockBehavior.Strict);

        Expression> sendMock = (msmq) => msmq.Send(It.IsAny()); //message => messages.Enqueue(message);
        mockQueue.Setup(sendMock).Callback(sendDelegate);

        Expression> receiveMock = (msmq) => msmq.Receive(It.IsAny(), It.IsAny());
        mockQueue.Setup(receiveMock).Returns(receiveDelegate);

        return mockQueue;
    }
0
adăugat
Am gasit o solutie si am postat aici: dotnetmonkey.net/post/Hard-to -Moq.aspx
adăugat autor Adam Lenda, sursa

Am întâmpinat această problemă recent și după ce am citit / căutat pe web, pare că nu există o cale simplă în afară de a utiliza un alt instrument așa cum am menționat mai sus. Sau crud de manipulare lucruri ca mine:

  • Creați instanță de clasă sigilată fără a fi apelat la constructor.
  • System.Runtime.Serialization.FormatterServices.GetUninitializedObject (instanceType);

  • Atribuiți valori proprietăților / câmpurilor prin reflecție

  • YourObject.GetType() .GetProperty ("PropertyName") SetValue (dto, newValue, null);
  • YourObject.GetType() GetField ("FieldName") SetValue (dto, newValue);
0
adăugat
Aceasta este singura soluție care are sens și funcționează pentru batjocorirea chestiilor interne ale Microsoft fără adăugarea de biblioteci suplimentare precum Fake-uri - mulțumesc!
adăugat autor Ben, sursa