एक इकाई को हैशकोड-समान अनुबंध का परीक्षण कैसे करना चाहिए?


79

संक्षेप में, जावा के ऑब्जेक्ट के अनुसार हैशकोड अनुबंध। शशकोड ():

  1. हैश कोड तब तक नहीं बदलना चाहिए जब तक कि कुछ बराबर () परिवर्तनों को प्रभावित न करे
  2. बराबर () का तात्पर्य हैश कोड == हैं

मान लें कि मुख्य रूप से अपरिवर्तनीय डेटा ऑब्जेक्ट्स में रुचि है - उनके निर्माण के बाद उनकी जानकारी कभी नहीं बदलती है, इसलिए # 1 को धारण करने के लिए माना जाता है। यह # 2 छोड़ देता है: समस्या बस यह पुष्टि करने में से एक है कि हैश कोड == के बराबर है।

जाहिर है, हम प्रत्येक बोधगम्य डेटा ऑब्जेक्ट का परीक्षण नहीं कर सकते जब तक कि सेट तुच्छ रूप से छोटा न हो। तो, यूनिट परीक्षण लिखने का सबसे अच्छा तरीका क्या है जो सामान्य मामलों को पकड़ने की संभावना है?

चूंकि इस वर्ग के उदाहरण अपरिवर्तनीय हैं, इसलिए इस तरह की वस्तु के निर्माण के सीमित तरीके हैं; यदि संभव हो तो यह इकाई परीक्षण उन सभी को कवर करना चाहिए। मेरे सिर के ऊपर से, प्रवेश बिंदु भवन निर्माता, डिसेरिएलाइज़ेशन, और उपवर्गों के निर्माता हैं (जो कि निर्माता को कॉल समस्या से छुटकारा दिलाने वाला होना चाहिए)।

[मैं अनुसंधान के माध्यम से अपने स्वयं के प्रश्न का उत्तर देने का प्रयास करने जा रहा हूं। अन्य StackOverflowers से इनपुट इस प्रक्रिया के लिए एक स्वागत योग्य सुरक्षा तंत्र है।]

[यह अन्य OO भाषाओं पर लागू हो सकता है, इसलिए मैं उस टैग को जोड़ रहा हूं।]


1
मुझे आमतौर पर लगता है कि अनुबंध पहले से ही या तो समान या हैशकोड लागू करने के कारण टूट गया, लेकिन दोनों नहीं। ओपनपोजो एक जावा प्रोजेक्ट में मदद करता है। EqualsAndHashCodeMatchRule इसके साथ मदद करता है। पहले से मौजूद उत्तर बाकी अनुबंधों के परीक्षण के बारे में पर्याप्त विवरण प्रदान करते हैं।
मिकिएल लीगवाटर

जवाबों:


73

EqualsVerifier एक अपेक्षाकृत नया ओपन सोर्स प्रोजेक्ट है और यह बराबरी के अनुबंध का परीक्षण करने में बहुत अच्छा काम करता है। यह मुद्दों से नहीं है बराबरी GSBase से परीक्षक है। मेरे द्वारा निश्चित रूप से इसकी सलाह दी जाएगी।


7
बस EqualsVerifier का उपयोग कर मुझे जावा के बारे में कुछ सिखाया!
डेविड

मैं पागल हो रहा हूँ? EqualsVerifier को मान ऑब्जेक्ट शब्दार्थ की बिल्कुल भी जाँच नहीं हुई! यह सोचता है कि मेरी कक्षा सही तरीके से लागू होती है (), लेकिन मेरी कक्षा डिफ़ॉल्ट "==" व्यवहार का उपयोग करती है। यह कैसे हो सकता है?! मुझे कुछ आसान याद आ रहा है।
जेबी रेन्सबर्गर

अहा! अगर मैं बराबरी से आगे नहीं बढ़ता (), तो EqualsVerifier मानता है कि सब कुछ ठीक है !? यही मैं उम्मीद नहीं करूंगा। मैं समान व्यवहार () के ओवरराइडिंग नहीं के बराबर () के लिए समान व्यवहार की अपेक्षा करता हूं (बिल्कुल) एक ही चीज सुपर.इसे के बराबर करने के लिए ()। अजीब।
बजे जेबी रेन्सबर्गर

7
@JBRainsberger हाय! यहाँ EqualsVerifier के निर्माता। मुझे खेद है कि आपको यह भ्रामक लगता है। पुन: बराबरी नहीं: आप "allFieldsShouldBeUsed ()" के साथ अपने अपेक्षित व्यवहार को सक्षम कर सकते हैं। आपके द्वारा बताए गए कारणों से अगले संस्करण में यह डिफ़ॉल्ट होगा। समान उदाहरण: मुझे नहीं लगता कि मैं कुछ उदाहरण कोड देखे बिना आपकी मदद कर सकता हूं। क्या आप कृपया एक नए प्रश्न में, या समूहों में EV मेलिंग सूची पर विस्तृत कर सकते हैं। www.forum/?fggroups#?forum/equalsverifier ? धन्यवाद!
jqno

1
EqualsVerifier वास्तव में शक्तिशाली है। मैंने वस्तुओं के बराबर नहीं होने के प्रत्येक संभावित संयोजन का परीक्षण नहीं करके एक बड़ी मात्रा में समय प्राप्त किया। त्रुटि संदेश वास्तव में सटीक और शिक्षाप्रद हैं। और सत्यापनकर्ता बहुत विन्यास योग्य है यदि आपका कोडिंग सम्मेलन डिफ़ॉल्ट अपेक्षा से अलग है। ग्रेट जॉब @jqno
gontard

11

मेरी सलाह यह सोचने की होगी कि क्यों / कैसे यह कभी सच नहीं हो सकता है, और फिर कुछ यूनिट परीक्षण लिखें जो उन स्थितियों को लक्षित करते हैं।

उदाहरण के लिए, मान लें कि आपके पास एक कस्टम Setवर्ग है। दो सेट समान होते हैं यदि उनमें समान तत्व होते हैं, लेकिन दो समान सेटों के अंतर्निहित डेटा संरचनाओं के लिए यह संभव है कि उन तत्वों को एक अलग क्रम में संग्रहीत किया जाए। उदाहरण के लिए:

MySet s1 = new MySet( new String[]{"Hello", "World"} );
MySet s2 = new MySet( new String[]{"World", "Hello"} );
assertEquals(s1, s2);
assertTrue( s1.hashCode()==s2.hashCode() );

इस मामले में, आपके द्वारा लागू किए गए हैशिंग एल्गोरिथ्म के आधार पर, सेट में तत्वों का क्रम उनके हैश को प्रभावित कर सकता है। तो यह इस तरह की परीक्षा है, जैसा कि मैं लिखूंगा, क्योंकि यह उस मामले का परीक्षण करता है जहां मुझे पता है कि कुछ हैशिंग एल्गोरिदम के लिए संभव होगा कि मैंने दो वस्तुओं के लिए अलग-अलग परिणामों का उत्पादन किया हो, जिन्हें मैंने बराबर माना है।

आपको अपने स्वयं के कस्टम वर्ग के साथ एक समान मानक का उपयोग करना चाहिए, जो कुछ भी हो।


6

यह इसके लिए जूनियर ऐडऑन का उपयोग करने के लायक है। वर्ग की जाँच करें EqualsHashCodeTestCase http://junit-addons.sourceforge.net/ आप इसे बढ़ा सकते हैं और createInstance और createNotEqualInstance कार्यान्वित कर सकते हैं, यह बराबर की जाँच करेगा और हैशकोड विधियाँ सही हैं।


4

मैं GSBase से EqualsTester की सिफारिश करूंगा। यह मूल रूप से वही करता है जो आप चाहते हैं। मुझे इसके साथ दो (छोटी) समस्याएं हैं:

  • कंस्ट्रक्टर सभी काम करता है, जिसे मैं अच्छा अभ्यास नहीं मानता।
  • यह तब विफल हो जाता है जब कक्षा ए का एक उदाहरण वर्ग ए के एक उपवर्ग के उदाहरण के बराबर होता है। यह जरूरी नहीं कि समान अनुबंध का उल्लंघन हो।

2

[इस लेखन के समय, तीन अन्य उत्तर पोस्ट किए गए थे।]

दोहराने के लिए, मेरे प्रश्न का उद्देश्य यह पुष्टि करने के लिए परीक्षणों के मानक मामलों को खोजना है hashCodeऔर equalsएक-दूसरे के साथ सहमत हैं। इस प्रश्न के लिए मेरा दृष्टिकोण प्रोग्रामर द्वारा उठाए गए सामान्य रास्तों की कल्पना करना है, जब प्रश्न में कक्षाएं लिखी जाती हैं, अर्थात् अपरिवर्तनीय डेटा। उदाहरण के लिए:

  1. equals()बिना लिखे लिखा hashCode()इसका मतलब अक्सर समानता को दो उदाहरणों के क्षेत्रों की समानता के रूप में परिभाषित किया गया था।
  2. hashCode()बिना लिखे लिखा equals()इसका मतलब यह हो सकता है कि प्रोग्रामर एक अधिक कुशल हैशिंग एल्गोरिथ्म चाह रहा था।

# 2 के मामले में, समस्या मुझे कोई भी नहीं लगती है। कोई अतिरिक्त उदाहरण नहीं बनाए गए हैं equals(), इसलिए किसी भी अतिरिक्त उदाहरण के लिए समान हैश कोड की आवश्यकता नहीं है। सबसे खराब रूप से, हैश एल्गोरिथ्म हैश मानचित्रों के लिए खराब प्रदर्शन प्राप्त कर सकता है, जो इस प्रश्न के दायरे से बाहर है।

# 1 के मामले में, मानक इकाई परीक्षण एक ही वस्तु के दो उदाहरणों को बनाने के लिए मजबूर करता है, समान डेटा के साथ कंस्ट्रक्टर को पास किया जाता है, और समान हैश कोड को सत्यापित करता है। झूठे सकारात्मक के बारे में क्या? कंस्ट्रक्टर मापदंडों को चुनना संभव है, जो कि एक गैर-अनसाउथ एल्गोरिथ्म पर समान हैश कोड उत्पन्न करने के लिए होता है। इस तरह के मापदंडों से बचने के लिए एक इकाई परीक्षण इस प्रश्न की भावना को पूरा करेगा। यहां शॉर्टकट के लिए स्रोत कोड का निरीक्षण equals()करना, कठिन सोचना, और उसके आधार पर एक परीक्षण लिखना है, लेकिन जब कुछ मामलों में यह आवश्यक हो सकता है, तो सामान्य परीक्षण भी हो सकते हैं जो सामान्य समस्याओं को पकड़ते हैं - और ऐसे परीक्षण भी भावना को पूरा करते हैं इस सवाल का।

उदाहरण के लिए, यदि परीक्षण किए जाने वाले वर्ग (इसे डेटा कहते हैं) में एक कंस्ट्रक्टर होता है, जो एक स्ट्रिंग लेता है, और स्ट्रिंग्स से निर्मित ऐसे equals()उदाहरण हैं जो उकसाए गए उदाहरण हैं equals(), तो एक अच्छा परीक्षण संभवतः परीक्षण करेगा:

  • new Data("foo")
  • एक और new Data("foo")

हम भी हैश कोड की जाँच कर सकते हैं new Data(new String("foo")), स्ट्रिंग को नजरअंदाज नहीं करने के लिए मजबूर करने के लिए, हालांकि यह एक सही हैश कोड Data.equals()उपज की तुलना में अधिक है, मेरी राय में, एक सही परिणाम प्राप्त करने के लिए है।

एली कोर्टराइट का जवाब equalsविनिर्देशन के ज्ञान के आधार पर हैश एल्गोरिथ्म को तोड़ने के तरीके से कठिन सोचने का एक उदाहरण है । एक विशेष संग्रह का उदाहरण एक अच्छा है, जैसा कि उपयोगकर्ता द्वारा निर्मित Collectionसमय पर बदल जाता है, और हैश एल्गोरिथ्म में muckups के लिए काफी प्रवण हैं।


1

यह केवल उन मामलों में से एक है जहां मेरे पास एक परीक्षण में कई दावे होंगे। चूंकि आपको समान पद्धति का परीक्षण करने की आवश्यकता है, इसलिए आपको उसी समय हैशकोड विधि की भी जांच करनी चाहिए। तो आपके प्रत्येक बराबर विधि परीक्षण के मामलों में हैशकोड अनुबंध की भी जाँच करें।

A one = new A(...);
A two = new A(...);
assertEquals("These should be equal", one, two);
int oneCode = one.hashCode();
assertEquals("HashCodes should be equal", oneCode, two.hashCode());
assertEquals("HashCode should not change", oneCode, one.hashCode());

और निश्चित रूप से एक अच्छे हैशकोड के लिए जाँच एक और व्यायाम है। ईमानदारी से मैं यह सुनिश्चित करने के लिए दोहरी जांच करने की जहमत नहीं उठाऊंगा कि हैशकोड एक ही रन में नहीं बदल रहा था, इस तरह की समस्या को कोड समीक्षा में पकड़ने और डेवलपर को समझने में मदद करने से बेहतर है कि क्यों यह एक अच्छा तरीका नहीं है हैशकोड विधियों को लिखने के लिए।



0

यदि मेरे पास एक वर्ग है Thing, जैसा कि अन्य लोग करते हैं, तो मैं एक वर्ग लिखता हूं ThingTest, जो उस वर्ग के लिए सभी यूनिट परीक्षण करता है। प्रत्येक ThingTestकी एक विधि है

 public static void checkInvariants(final Thing thing) {
    ...
 }

और यदि Thingवर्ग हैशकोड को ओवरराइड करता है और बराबर होता है तो उसके पास एक विधि होती है

 public static void checkInvariants(final Thing thing1, Thing thing2) {
    ObjectTest.checkInvariants(thing1, thing2);
    ... invariants that are specific to Thing
 }

यह विधि उन सभी आक्रमणकारियों की जाँच करने के लिए ज़िम्मेदार है, जिन्हें किसी भी जोड़ीदार Thingवस्तुओं के बीच रखने के लिए डिज़ाइन किया गया है । जिस ObjectTestविधि को यह निर्दिष्ट करता है, वह सभी आक्रमणकारियों की जाँच करने के लिए ज़िम्मेदार है जो किसी भी जोड़ीदार वस्तुओं के बीच होनी चाहिए। के रूप में equalsऔर hashCodeसभी वस्तुओं के तरीके हैं, कि विधि की जाँच करता है hashCodeऔर equalsलगातार कर रहे हैं।

मेरे पास कुछ परीक्षण विधियां हैं जो Thingवस्तुओं के जोड़े बनाते हैं, और उन्हें युग्म checkInvariantsविधि में पास करते हैं । मैं समकक्ष विभाजन का उपयोग यह तय करने के लिए करता हूं कि जोड़े क्या परीक्षण के लायक हैं। मैं आमतौर पर प्रत्येक जोड़ी को केवल एक विशेषता में अलग करने के लिए बनाता हूं, साथ ही एक परीक्षण जो दो समकक्ष वस्तुओं का परीक्षण करता है।

मेरे पास कभी-कभी 3 तर्क checkInvariantsपद्धति भी होती है , हालांकि मुझे लगता है कि यह खोज दोषों में कम उपयोगी है, इसलिए मैं अक्सर ऐसा नहीं करता हूं


यह यहाँ एक और पोस्टर का उल्लेख करने के समान है: stackoverflow.com/a/190989/545127
Raedwald
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.