Bir arkadaş sınıfı kullanarak c ++ 'da birim test özel yöntemi


15

Bunun tartışmalı bir uygulama olduğunu biliyorum, ama bunun benim için en iyi seçenek olduğunu varsayalım. Bunu yapmak için gerçek tekniğin ne olduğunu merak ediyorum. Gördüğüm yaklaşım şudur:

1) Test etmek istediğim yöntemin sınıf arkadaş sınıfını yapın.

2) Arkadaş sınıfında, sınanan sınıfın özel yöntemlerini çağıran genel bir yöntem (ler) oluşturun.

3) Arkadaş sınıfının genel yöntemlerini test edin.

Yukarıdaki adımları açıklamak için basit bir örnek:

#include <iostream>

class MyClass
{
  friend class MyFriend; // Step 1

  private:
  int plus_two(int a)
  {
    return a + 2;
  }
};

class MyFriend
{
public:
  MyFriend(MyClass *mc_ptr_1)
  {
    MyClass *mc_ptr = mc_ptr_1;
  }

  int plus_two(int a) // Step 2
  {
    return mc_ptr->plus_two(a);
  }
private:
  MyClass *mc_ptr;
};

int main()
{
  MyClass mc;
  MyFriend mf(&mc);
  if (mf.plus_two(3) == 5) // Step 3
    {
      std::cout << "Passed" << std::endl;
    }
  else
    {
      std::cout << "Failed " << std::endl;
    }

  return 0;
}

Düzenle:

Görüşme, insanların kod tabanımı merak ettikleri cevaplardan birini izleyerek görüyorum.

Sınıfımda diğer yöntemlerle çağrılan yöntemler var; bu yöntemlerin hiçbiri sınıf dışında çağrılmamalı, bu yüzden özel olmalıdır. Tabii ki tek bir yönteme konabilirler, ancak mantıksal olarak çok daha iyi ayrılırlar. Bu yöntemler birim testini gerektirecek kadar karmaşıktır ve performans sorunları nedeniyle bu yöntemleri yeniden hesaba katmak zorunda kalacağım, bu nedenle yeniden faktoringimin hiçbir şeyi bozmadığından emin olmak için bir test yaptırmak güzel olurdu. Ekip üzerinde çalışan tek kişi ben değilim, ancak testler de dahil olmak üzere bu proje üzerinde çalışan tek kişi benim.

Yukarıdakileri söyledikten sonra, sorum, geri bildirimi takdir etsem de, özel yöntemler için birim testleri yazmanın iyi bir uygulama olup olmadığıyla ilgili değildi.


5
İşte şüpheli bir soru için şüpheli bir öneri. Ben daha sonra yayınlanan kod testi hakkında bilmek zorunda olduğu gibi arkadaşın kaplin sevmiyorum. Nir'un aşağıdaki cevabı, bunu hafifletmenin bir yoludur, ancak yine de sınıfı teste uyacak şekilde değiştirmeyi sevmiyorum. Sıklıkla mirasa güvenmediğimden, bazen sadece özel yöntemlerin korunmasını sağlarım ve gerektiğinde bir test sınıfını miras alıp açığa çıkarıyorum. Bu yorum için en az üç "boo hisses" bekliyorum, ancak gerçek şu ki genel API ve test API'sı özel API'dan farklı olabilir ve yine de farklı olabilir. Meh.
J Trana

4
@JTrana: Bunu neden doğru bir cevap olarak yazmıyorsunuz?
Bart van Ingen Schenau

Tamam. Bu, gurur duyduğunuz kişilerden biri değil, ama umarım yardımcı olacaktır.
J Trana

Yanıtlar:


23

Sık kullandığım bir arkadaşa alternatif (bir anlamda) access_by olarak tanıdığım bir model. Oldukça basit:

class A {
  void priv_method(){};
 public:
  template <class T> struct access_by;
  template <class T> friend struct access_by;
}

Şimdi, B sınıfının A testine dahil olduğunu varsayalım. Bunu yazabilirsiniz:

template <> struct access_by<B> {
  call_priv_method(A & a) {a.priv_method();}
}

Daha sonra, A'nın özel yöntemlerini çağırmak için bu access_by uzmanlığını kullanabilirsiniz. Temel olarak, A'nın özel yöntemlerini çağırmak isteyen sınıfın başlık dosyasına arkadaşlık bildirme sorumluluğu ortaya çıkar. Ayrıca A'nın kaynağını değiştirmeden arkadaşlarınızı A'ya eklemenizi sağlar. İdiyomatik olarak, A'nın kaynağını kimin okuduğunu da A'nın B'yi arayüzünü genişletme anlamında gerçek bir arkadaş göstermediğini gösterir. Aksine, A'nın arayüzü verildiği gibi tamamlanır ve B'nin A'ya özel erişime ihtiyacı vardır (test iyi bir örnektir, ayrıca destek python bağlamaları uygularken bu kalıbı kullandım, bazen C ++ 'da özel olması gereken bir işlev uygulama için python katmanına maruz bırakmak).


friend access_byİlk arkadaş olmayanı yeterli yapmak için geçerli bir kullanım vakası mı merak ediyorsunuz - A içindeki her şeye erişebilecek iç içe bir yapı mı? Örneğin. coliru.stacked-crooked.com/a/663dd17ed2acd7a3
keskin

10

Test etmek zorsa, kötü yazılmış

Kendi testlerini garanti edecek kadar karmaşık özel yöntemlere sahip bir sınıfınız varsa, sınıf çok fazla şey yapıyor. İçeride dışarı çıkmaya çalışan başka bir sınıf daha var.

Test etmek istediğiniz özel yöntemleri yeni bir sınıfa (veya sınıflara) çıkarın ve herkese açık hale getirin. Yeni sınıfları test edin.

Kodun test edilmesini kolaylaştırmanın yanı sıra, bu yeniden düzenleme kodun anlaşılmasını ve bakımını kolaylaştıracaktır.


1
Bu yanıta tamamen katılıyorum, özel yöntemlerinizi genel yöntemleri test ederek tamamen test edemezseniz, bir şey doğru değildir ve özel yöntemleri kendi sınıflarına göre yeniden düzenlemek iyi bir çözüm olacaktır.
David Perfors

4
Benim kod tabanı, bir sınıf bir hesaplama grafiği başlatan çok karmaşık bir yöntemi vardır. Bunun çeşitli yönlerini başarmak için sırayla birkaç alt işlevi çağırır. Her alt fonksiyon oldukça karmaşıktır ve kodun toplamı çok karmaşıktır. Ancak, bu sınıfta ve doğru sırada çağrılmadıysa alt işlevler anlamsızdır. Kullanıcının ilgilendiği tek şey, hesaplama grafiğinin tamamen başlatılmasıdır; ara ürünler kullanıcı için değersizdir. Lütfen, bunu nasıl yeniden düzenlemem gerektiğini ve neden sadece özel yöntemleri test etmekten daha mantıklı olduğunu duymak istiyorum.
Nir Friedman

1
@Nir: Önemsiz olanı yapın: tüm bu yöntemlerle herkese açık bir sınıf çıkarın ve mevcut sınıfınızı yeni sınıfın etrafında bir cephe yapın.
kevin cline

Bu cevap doğrudur, ancak gerçekten birlikte çalıştığınız verilere bağlıdır. Benim durumumda gerçek test verileri sağlanmadı, bu yüzden gerçek zamanlı verileri gözlemleyerek ve daha sonra bunu uygulamama "enjekte ederek" ve Tek bir veri parçası işlenemeyecek kadar karmaşık olduğundan, gerçek zamanlı verileri yeniden oluşturmaktan her birini hedeflemek için gerçek zamanlı verilerin yalnızca bir alt kümesi olan kısmi test verilerini yapay olarak oluşturmak çok daha kolay olacaktır.Özel işlev karmaşık değildir birkaç diğer küçük sınıf (ilgili işlevsellik ile) uygulanması için çağrı yeterli
rbaleksandar

4

Özel yöntemleri test etmemelisiniz. Dönemi. Sınıfınızı kullanan sınıflar, çalışmak için kaputun altında kullandıkları yöntemleri değil, yalnızca sağladığı yöntemleri önemser.

Kod kapsamınız hakkında endişeleniyorsanız, bu özel yöntemi genel yöntem çağrılarından birinden test etmenizi sağlayan yapılandırmalar bulmanız gerekir. Bunu yapamazsanız, yöntemin ilk etapta olmasının anlamı nedir? Sadece ulaşılamaz bir kod.


6
Özel yöntemlerin amacı kalkınmayı kolaylaştırmaktır (endişelerin ayrılması ya da KURU ya da herhangi bir şeyin sürdürülmesi yoluyla), ancak kalıcı olmaması amaçlanmaktadır. Bu nedenle özeldirler. Bir uygulamadan diğerine büyük ölçüde görünebilir, kaybolabilir veya işlevselliği değişebilirler, bu yüzden bunları birim testlere bağlamak her zaman pratik veya hatta kullanışlı değildir.
Ampt

8
Her zaman pratik veya kullanışlı olmayabilir, ancak bu onları asla test etmemeniz gerektiğini söylemekten çok uzaktır. Özel yöntemler hakkında sanki başka birinin özel yöntemleri gibi konuşuyorsunuz; "Görünebilirler, yok olabilirler ...". Hayır, yapamadılar. Bunları doğrudan birim olarak test ediyorsanız, bunun nedeni yalnızca onları kendinizin bakımını yapmanızdır. Uygulamayı değiştirirseniz, testleri değiştirirsiniz. Kısacası, battaniye beyanınız haksız. OP'yi bu konuda uyarmak iyi olsa da, sorusu hala haklı ve cevabınız aslında cevap vermiyor.
Nir Friedman

2
Şunu da not edeyim: OP, bunun tartışmalı bir uygulama olduğunun farkında olduğunu söyledi. Eğer yine de yapmak istiyorsa, bunun için gerçekten iyi nedenleri olabilir mi? İkimiz de kod tabanının ayrıntılarını bilmiyoruz. Çalıştığım kodda, çok deneyimli ve uzman programcılarımız var ve bazı durumlarda özel yöntemleri birim test etmenin yararlı olduğunu düşünüyorlardı.
Nir Friedman

2
-1, "Özel yöntemleri test etmemelisiniz" gibi bir cevap. IMHO yardımcı olmuyor. Özel yöntemlerin ne zaman test edileceği ve ne zaman test edilmeyeceği konusu bu sitede yeterince tartışılmıştır. OP bu tartışmanın farkında olduğunu göstermiştir ve açıkça kendi durumunda özel yöntemleri test etmenin yolu olduğu varsayımı altında çözümler aramaktadır.
Doc Brown

3
Bence buradaki problem çok klasik bir XY Sorunu olabilir , çünkü OP özel yöntemlerini bir nedenden ötürü test etmeyi düşündüğü için, gerçekte sınava daha pragmatik bir bakış açısıyla yaklaşıp özel sınıfın son kullanıcıları ile sözleşme olan halka açık olanlar için sadece yardımcı fonksiyonlar gibi yöntemler.
Ampt

3

Bunu yapmak için birkaç seçenek vardır, ancak dahili uygulama ayrıntılarına erişmenizi sağlamak için modül modüllerin genel arayüzünü değiştirdiklerini (ünite testini etkili bir şekilde bağlı olduğunuz müşteri bağımlılıklarına etkili bir şekilde dönüştürmek) unutmayın. hiç bağımlılık yok ).

  • test edilen sınıfa bir arkadaş (sınıf veya işlev) bildirimi ekleyebilirsiniz.

  • test edilen kodu çalıştırmadan #define private publicönce test dosyalarınızın başına ekleyebilirsiniz #include. Test edilen kodun zaten derlenmiş bir kütüphane olması durumunda, bu başlıkların artık derlenmiş ikili kodla eşleşmemesine neden olabilir (ve UB'ye neden olabilir).

  • test edilen sınıfınıza bir makro ekleyebilir ve daha sonraki bir tarihte bu makronun ne anlama geldiğine karar verebilirsiniz (test kodu için farklı bir tanımla). Bu, dahili olanları test etmenize olanak tanır, ancak üçüncü taraf istemci kodunun sınıfınıza girmesine de izin verir (eklediğiniz bildirimde kendi tanımlarını oluşturarak).


2

İşte şüpheli bir soru için şüpheli bir öneri. Ben daha sonra yayınlanan kod testi hakkında bilmek zorunda olduğu gibi arkadaşın kaplin sevmiyorum. Nir'un cevabı bunu hafifletmenin bir yoludur, ancak yine de sınıfı teste uymak için değiştirmekten pek hoşlanmıyorum.

Sıklıkla mirasa güvenmediğimden, bazen sadece özel yöntemlerin korunmasını sağlarım ve gerektiğinde bir test sınıfını miras alıp açığa çıkarıyorum. Gerçek şu ki, genel API ve test API'sı özel API'dan farklı olabilir ve yine de farklı olabilir, bu da sizi bir çeşit bağlama bırakır.

İşte bu hile için başvurduğum türden pratik bir örnek. Gömülü kod yazıyorum ve devlet makinelerine biraz güveniyoruz. Harici API mutlaka dahili durum makine durumu hakkında bilgi sahibi olmak zorunda değildir, ancak test tasarım belgesindeki durum makine şemasına uygunluğu (tartışmalı olarak) test etmelidir. Bir "mevcut durum" alıcısını korumalı olarak gösterebilir ve daha sonra test erişimini verebilirim, böylece durum makinesini daha iyi test etmeme izin verebilirim. Genellikle bu tür bir sınıfı kara kutu olarak test etmekte zorlanırım.


Bu bir Java yaklaşımı olsa da, aynı işlevi (test sınıfları) aynı sınıftaki diğer sınıfların görebilmesini sağlamak yerine, özel işlevleri varsayılan düzeyde yapmak oldukça standarttır.

0

Arkadaşlarınızı kullanmak zorunda kalmamak için kodunuzu birçok geçici çözümle yazabilirsiniz.

Sınıf yazabilir ve hiçbir zaman özel yöntem kullanamazsınız. Daha sonra tek yapmanız gereken derleme birimi içinde uygulama işlevleri yapmak, sınıfınızın onları çağırmasına ve erişmeleri gereken veri üyelerini iletmesine izin vermektir.

Ve evet, imzalarınızı değiştirebileceğiniz veya ileride başlığınızı değiştirmeden yeni "uygulama" yöntemleri ekleyebileceğiniz anlamına gelir.

Buna değer olup olmadığına rağmen tartmak zorundasınız. Ve çok şey gerçekten başlığınızı kimin göreceğine bağlı olacaktır.

Üçüncü taraf bir kütüphane kullanıyorsam, birim test cihazlarına yönelik arkadaş bildirimlerini görmek istemem. Ben de onların kütüphane inşa etmek istemiyorum ve ben yaptığımda testleri çalıştırmak. Ne yazık ki, inşa ettiğim çok sayıda 3. taraf açık kaynak kütüphanesi bunu yapıyor.

Test, kütüphane yazarlarının işi değil, kullanıcılarıdır.

Ancak, tüm sınıflar kitaplığınızın kullanıcısı tarafından görülemez. Sınıfların birçoğu "uygulama" dır ve düzgün çalışmasını sağlamak için bunları en iyi şekilde uygularsınız. Bunlarda, hala özel yöntemleriniz ve üyeleriniz olabilir, ancak birim test uzmanlarının bunları test etmesini isteyebilirsiniz. Öyleyse devam edin ve sağlam kod daha hızlı yol açacaksa bu şekilde yapın, bunu yapmak isteyenler için bakımı kolaydır.

Sınıfınızın kullanıcıları kendi şirketiniz veya ekibiniz içindeyse, şirketinizin kodlama standartlarının izin verdiği varsayılarak bu strateji hakkında biraz daha rahatlayabilirsiniz.

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.