Katta va murakkab sinflarda birlik testlarini qanday qo'llash mumkin?

I'm implementing unit tests in a finance system that involves several calculations. One of the methods, receives an object by parameter with more than 100 properties, and based on this object's properties, it calculates the return. In order to implement unit tests for this method, I need to have all of this object filled with valid values.

So...question: today this object is populated via database. On my Unit Tests (I'm using NUnit), I'd need to avoid the database and create a mock object of that, to test only the method's return. How can I efficiently test this method with this huge object? Do I really need to manually fill all of the 100 properties of it? Is there a way to automatize this object creation using Moq (for example)?

obs: I'm writing unit tests for a system that is already created. It's not feasible to re-write all the architecture at this moment.
Thanks a million!

15
qo'shib qo'ydi muallif durron597, manba
Menda bu erda yaxshi bo'lganlarni kiritish uchun javobim yo'q, lekin faqat ko'tarish kerak bo'lgan nuqta - bu TDD da "Dizayn" D. Biror narsa sinovdan o'tishi qiyin bo'lsa, kodni loyihalashda sizda biror narsa noto'g'ri.
qo'shib qo'ydi muallif Carl Manaster, manba
@VincentSavard boshqa saytlarga murojaat qilganda, ko'pincha o'zaro faoliyat joylashuvga moslashtiriladi
qo'shib qo'ydi muallif gnat, manba
Buning o'rniga savolingizni softwareengineering.stackexchange.com ga joylashtirishni o'ylab ko'rishingiz mumkin, menimcha, bu yanada qulayroq bo'ladi.
qo'shib qo'ydi muallif Vincent Savard, manba
@VincentSavard sizning fikringizdan tashqari, foydalanuvchilarni qidirish ni yuborishdan oldin boshqa saytga taklif qilishdan ham yaxshiroq bo'lardi. Bu savol, ehtimol, dasturiy ta'minotni muhandisligi uchun ikki nusxada yopilgan bo'lishi mumkin, shuningdek, 64 ta o'xshash savollardan olingan ma'lumotni .
qo'shib qo'ydi muallif user439793, manba

6 javoblar

Agar bu 100 qiymat ahamiyatli bo'lmasa va ulardan faqat ba'zilariga kerak bo'lsa, unda bir nechta variant bor.

Siz yangi ob'ektni yaratishingiz mumkin (funktsiya tamsayılar uchun null va satrlari uchun 0 kabi standart qiymatlar bilan boshlanadi) va faqat kerakli xususiyatlarni belgilashingiz mumkin:

 var obj = new HugeObject();
 obj.Foo = 42;
 obj.Bar = "banana";

Ob'ektdagi barcha xususiyatlar uchun qo'g'irchoqli qiymatlarni belgilaydigan AutoFixture kabi ba'zi kutubxonalardan foydalanishingiz mumkin:

 var fixture = new Fixture();
 var obj = fixture.Create();

Kerakli xususiyatlarni qo'lda belgilashingiz mumkin yoki siz fikstür quruvchisini foydalanishingiz mumkin

 var obj = fixture.Build()
                  .With(o => o.Foo, 42)
                  .With(o => o.Bar, "banana")
                  .Create();

Another useful library for same purpose is NBuilder


QAYD: Agar barcha funktsiyalar siz tekshirayotgan xususiyatga mos bo'lsa va ular ma'lum qiymatlarga ega bo'lsa, test uchun zarur bo'lgan qiymatlarni aniqlaydigan hech qanday kutubxona mavjud emas. Bitta usul test qiymatlarini qo'lda belgilaydi. Har bir testdan oldin ba'zi bir standart qiymatlarni o'rnatib, muayyan test uchun kerakli narsani o'zgartirsangiz, juda ko'p ishni bartaraf etsangiz ham. Ie. oldindan belgilangan qiymatlar to'plamiga ega ob'ektni yaratadigan yordamchi usul (lar) ni yaratish:

 private HugeObject CreateValidInvoice()
 {
     return new HugeObject {
         Foo = 42,
         Bar = "banaba",
         //...
     };
 }

Va keyin sizning testingizda faqat ayrim joylarni bekor qilish mumkin:

 var obj = CreateValidInvoice();
 obj.Bar = "apple";
//...
12
qo'shib qo'ydi
To'g'ri qadriyatlar kerak bo'lsa, qanday kutubxonada kerak bo'lgan qadriyatlarni bilib olasiz? Bunday qiymatlarni qo'lda ta'minlashingiz kerak
qo'shib qo'ydi muallif Sergey Berezovskiy, manba
@LisaShiphra Iltimos, mening yangilangan javobni ko'ring - siz ba'zi qiymatlar bilan to'ldirilgan ob'ektni yaratadigan yaratilish usullaridan foydalanishingiz mumkin. Men sizning "populyatsiyalarni yaratish" deb atashga ishonaman
qo'shib qo'ydi muallif Sergey Berezovskiy, manba
Hey Sergey. Javob uchun birinchi o'rinda rahmat. Afsuski, usulni sinab ko'rish uchun men ushbu ob'ektlarning barchasiga ega bo'lishim kerak. Uslubni hisoblash natijalarini sinab ko'rish uchun aniq natijalarni olish uchun tegishli yozuvlarga ega bo'lishim kerak. Shuning uchun men tasodifiy kirishlardan foydalanolmayman. NBuilder bunga yordam beradimi?
qo'shib qo'ydi muallif Lisa Shiphrah, manba
Bu mening fikrim. Men 100dan ortiq qo'llarni to'ldirishni ushbu ob'ektni joylashtirishning yagona usuli deb hisoblayman. Rostini aytganda, men hatto kutubxonani qidirib topolmaydigan ob'ektni oladigan va men uchun "aholi populyatsiyasi" ni yaratgan. Sqlda ba'zi jadvalni joriy kontentning ma'lumotlarni kiritish skriptlarini yaratish uchun so'rashga o'xshash.
qo'shib qo'ydi muallif Lisa Shiphrah, manba
Sergey, men sizning takliflaringiz + AutoFixture bilan kecha qo'lda yechim o'tkazdim. Agar muammoimga javob beradigan bo'lsak, albatta: "Ob'ektni qo'l bilan to'ldirishga to'g'ri keladi", men siz taklif qilganingizdek, maydonlarni standart qiymatlari bilan avto-qiymatlarni yaratish va AutoFixture bilan harakatni kamaytirishga urinmay qolaman. Rahmat!
qo'shib qo'ydi muallif Lisa Shiphrah, manba

Sinov uchun katta miqdordagi haqiqiy ma'lumotni olish kerak bo'lgan holatlar uchun, ma'lumotlarni JSONga ketma-ket o'tkazib, to'g'ridan-to'g'ri sinov darslariga qo'ydim. Asl ma'lumotlar sizning ma'lumotlar bazasidan olinishi va keyinchalik seriyali bo'lishi mumkin. Shunga o'xshash narsa:

[Test]
public void MyTest()
{
   //Arrange
    var data = GetData();

   //Act
    ... test your stuff

   //Assert
    .. verify your results
}


public MyBigViewModel GetData()
{
    return JsonConvert.DeserializeObject(Data);
}

public const String Data = @"
{
    'SelectedOcc': [29, 26, 27, 2,  1,  28],
    'PossibleOcc': null,
    'SelectedCat': [6,  2,  5,  7,  4,  1,  3,  8],
    'PossibleCat': null,
    'ModelName': 'c',
    'ColumnsHeader': 'Header',
    'RowsHeader': 'Rows'
   //etc. etc.
}";

Bu kabi testlar juda ko'p bo'lsa, bu maqbul bo'lmasligi mumkin, chunki bu formatdagi ma'lumotlarni olish ancha vaqt talab etadi. Ammo, bu sizga ketma-ket ishga tushirilgandan so'ng turli xil testlar uchun o'zgartirishingiz mumkin bo'lgan asosiy ma'lumotni berishi mumkin.

Ushbu JSONni olish uchun siz ushbu buyuk ob'ekt uchun ma'lumotlar bazasini alohida so'rashi kerak, uni JSONga JsonConvert.Serialise orqali ketma-ket yozib qo'ying va ushbu satrni manba kodingizga yozing - bu bit nisbatan oson, lekin Biroz vaqtni qo'l bilan qilish kerak, chunki ... faqat bir marta.

Hisobotni taqdim etishni testdan o'tkazish va JB dan ma'lumotlarni olish hozirgi test uchun tashvishlanmasa, men ushbu texnikani muvaffaqiyatli ishlatganman.

p.s. you'll need Newtonsoft.Json package to use JsonConvert.DeserializeObject

4
qo'shib qo'ydi

Cheklovlarni hisobga olgan holda (yomon kodni loyihalashtirish va texnik qarz ... Men bola) Birlik sinovi qo'lda joylashtirish juda qiyin bo'ladi. Hibrid integratsiya testi zarur bo'lib, siz aniq ma'lumot manbasini (ishlab chiqarishda emas, balki) urish kerak.

Potensial iksirlar

  1. Ma'lumotlar bazasining nusxasini oling va qaram murakkab sinfni to'ldirish uchun kerakli jadvallar/ma'lumotlarni joylashtiring. Umid qilamanki, kod modullashtirilgan bo'lib, ma'lumotlar uzatishda murakkab kompleksni olish va to'ldirish kerak.

  2. Ma'lumotlarni kirishni taqiqlang va kerakli ma'lumotlarni boshqa muqobil manbalardan (tekis fayl, ehtimol? csv) import qiling.

Boshqa barcha kodlar birlik sinovini amalga oshirish uchun zarur bo'lgan boshqa bog'liqliklarni masxara qilishga qaratilishi mumkin.

Chapdagi boshqa variantni qo'lda cheklashni taqiqlash.

Yondan tashqari, bu buzuq kodning barcha qismini hidlaydi, lekin bu OPning doirasidan tashqarida emas, chunki u hozirgi vaqtda o'zgarib bo'lmaydi. Men buni qaror qabul qilganlar uchun eslatib qo'yishni taklif qilaman.

4
qo'shib qo'ydi

Bu yondashuvni qabul qilaman:

1 - 100 funktsiya parametrlari obyektining har bir kombinatsiyasi uchun Write birligi sinovlari, siz uchun bu vositani qo'llagan holda (masalan, pex, intellitest) va ularning barchasi yashil rangda ishlaydi. Ushbu nuqtada keyinchalik aniq ko'rinadigan sabablarga ko'ra birlik sinovlari o'rniga, birlik sinovlari emas, balki integratsiya testlari deb nomlanadi.

2 - Kodni boshqa usullar deb ataydigan usullarni qayta ko'rib chiqing - boshqa kodlarga bog'liq emas, chunki ular boshqa usullarni chaqirmaydi. Qolgan usullar faqat integratsiyalashgan bo'lishi mumkin.

3 - Barcha integratsiya testlari yashil ishlayotganligini tekshiring.

4 - Yangi standart sinov kodi uchun yangi birlik sinovlari yarating.

5 - yashil rangda ishlaydigan har qanday narsalar sizning oldingizga kelib, sizning oldingizga qo'yib yuboradi.

6 - yashil rangda ishlaydigan har qanday narsa sizning qurilmangizdagi sinovlarda kerakli bo'lgan 100 ta xususiyatni faqat har bir alohida usul uchun talab qilinadigan darajaga kamaytirishga olib kelishi mumkin. Bu, ehtimol, qo'shimcha reaktoring uchun joylarni ta'kidlaydi, ammo baribir ob'ekt parametrlarini soddalashtiradi. Bu esa navbatdagi kodni ishlab chiquvchilarga kamroq avtomobillarni ishlab chiqarishga imkon beradi va men 50 ta xususiyatlar mavjud bo'lganida parametr ob'ektining o'lchamlarini ko'rib chiqish uchun tarixiy kamchiliklarni bartaraf etishni xohlayman. Endi bu masalani hal qilmaslik, oxirida 150 parametrga qadar o'sadi, bu unga qarshi turishga imkon beradi, hech kim xohlamaydi.

1
qo'shib qo'ydi

Birinchidan, birinchi navbatda, kod hozirda JB dan tortib olinayotgan bo'lsa, ushbu interfeys orqali amalga oshirilgan ushbu ob'ektning sotib olishni qilishingiz kerak. So'ngra siz interfeysi sizning qurilmangiz sinovlarida kerakli narsani qaytarish uchun masxara qilishingiz mumkin.

Men sizning poyabzalingizda bo'lganimda, hisoblash mantig'ini chiqarib, testlarni yangi "kalkulyator" klassiga yozib qo'ydim. Barchasini imkon qadar cheklab qo'ying. Kirish 100 funktsiyaga ega bo'lsa-da, ularning hammasi ham har bir hisoblash uchun tegishli emas - ularni ajratish uchun interfeyslar dan foydalaning. Bu kutilgan ko'rinishni paydo bo'ladi, kodni yaxshilaydi.

Shunday qilib, sizning sinfingiz BigClass deb nomlangan bo'lsa, siz ma'lum bir hisob-kitobda ishlatilishi mumkin bo'lgan interfeys yaratishingiz mumkin. Shu tarzda, mavjud klassni o'zgartirmaysiz yoki boshqa kod shu bilan ishlaydi. Olingan hisob-kitobning mantiqiyligi mustaqil, sinabroq va kod bo'lishi mumkin - juda oddiy.

    public class BigClass : ISet1
    {
        public string Prop1 { get; set; }
        public string Prop2 { get; set; }
        public string Prop3 { get; set; }
    }

    public interface ISet1
    {
        string Prop1 { get; set; }

        string Prop2 { get; set; }
    }

    public interface ICalculator
    {
        CalculationResult Calculate(ISet1 input)
    }
1
qo'shib qo'ydi
Rahmat Stefan. Lekin mening savolim ob'ektni qanday yaratishni emas, balki uni to'ldirishdir. Sizning namunangizda "BigClass" klassi int, strings, ro'yxatlar, boshqa sinflar bo'lishi mumkin bo'lgan 100 dan ortiq xususiyatlarga ega. Mening savolim - bu ob'ektni yaratish jarayonini optimallashtira olishim kerak, chunki bularning barchasi ma'lum natijalarga erishish uchun tegishli natijalarni yaratish uchun kerak.
qo'shib qo'ydi muallif Lisa Shiphrah, manba
Ob'ektni ma'lum bir hisob-kitob qilish uchun umuman yoqimsiz bo'lgan qadriyatlarga qanday qilib egalik qilish kerakligi haqida o'ylamasligim kerak. Buning o'rniga, sinashni istagan mantiqni "olib tashlashga" harakat qilishingiz kerak. Agar siz o'zingizning boshlang'ich sinfingizdan qat'i nazar, butun to'siqni sinab ko'rmoqchi bo'lsangiz, barcha 100 ta maydonni qo'lda o'rnatgan bo'lsangiz ham, testlar saqlanib qolmaydi. Mening taklifim "kalkulyator" ni chiqarib olish va u faqat kerakli qiymatni kutishini ta'minlash. Mening misolimda bu ISet. Sinovingizda siz BigClassning bir misolini yaratasiz va uni ISet-ga tashlaysiz, faqat siz qiziqadigan xususiyatlarni belgilashingiz mumkin.
qo'shib qo'ydi muallif Stefan Georgiev, manba

Qurilmangizdagi sinovlar uchun xotirada ma'lumotlar bazasidan foydalaning

Ya'ni ... bu sizning texnik sinovingiz degani emas, va sizning xotirangizdagi ma'lumotlar bazasidan foydalanib, test sinovlari emas, balki birlik sinovlari. Biroq, ba'zan imkonsiz cheklovlar bilan duch kelganida, men sizni bir joyga berishingiz kerak va bu o'sha paytlardan biri bo'lishi mumkin.

Mening takliflarim sizning test sinovida SQLite (yoki shunga o'xshash) dan foydalanish. Sizning bazangizni SQLite ma'lumotlar bazasiga chiqarish va ularni takrorlash uchun vositalar mavjud, u holda skriptlarni yaratish va ma'lumotlar bazasining xotirada saqlanadigan versiyasiga yuklashingiz mumkin. Siz o'zingizning "birlik" testlaringizdagi turli xil ma'lumotlar bazasi provayderini haqiqiy koddan ko'ra o'rnatish uchun qaramlik in'ektsiyasi va ombor naqshidan foydalanishingiz mumkin.

Shu tarzda, mavjud ma'lumotlaringizni ishlatishingiz mumkin, testlar uchun old shartlar sifatida kerak bo'lganda uni o'zgartirishingiz mumkin. Buning haqiqiy birlik testi emasligini tan olishingiz kerak ... ya'ni ma'lumotlar bazasi aslida qanday ishlab chiqilishi mumkinligi bilan cheklangan (ya'ni, jadval cheklovlari muayyan stsenariylarni sinab ko'rishga to'sqinlik qiladi), shuning uchun to'liq ma'noda birlik sinovini qila olmaysiz. Bundan tashqari, ushbu testlar sekinroq ishlaydi, chunki ular, albatta, ma'lumotlar bazasi ishlarini qilmoqdalar, shuning uchun siz ushbu testlarni bajarish uchun qo'shimcha vaqtni rejalashtirishingiz kerak bo'ladi. (Ular odatda juda tez bo'lsa ham.) Boshqa barcha narsalarni masxara qilishni unutmang (masalan, ma'lumotlar bazasiga qo'shimcha ravishda xizmat chaqiruvi mavjud bo'lsa, u hali ham salbiy salohiyatga ega).

Agar bu yondashuv siz uchun foydali bo'lsa, bu erda sizni olib ketish uchun ba'zi yo'nalishlar mavjud.

SQLite uchun SQLite Konverter:

https://www.codeproject.com/ Maqolalar/26932/Konvert-SQL-Server-DB-to-SQLite-DB

SQLite studio: https://sqlitestudio.pl/index.rvt

(Xotiradan foydalanish uchun ssenariylarni yaratish uchun foydalaning)

Xotirada foydalanish uchun quyidagilarni bajaring:

TestConnection = yangi SQLiteConnection ("FullUri = fayl :: xotira:? Kesh = birgalikda");

Ma'lumotlar bazasidan ma'lumotlar bazasi tuzilishi uchun alohida script mavjud, ammo bu shaxsiy parametrdir.

Bu yordam beradi va omad tilaymiz.

0
qo'shib qo'ydi