Go में यूनिट टेस्ट लिखते समय, फ़ाइल सिस्टम, सिस्टम क्लॉक या बाहरी कमांड जैसी बाहरी निर्भरताओं (external dependencies) को संभालना पेचीदा हो सकता है। यह पोस्ट इससे निपटने के तीन तरीकों की पड़ताल करती है, और अंततः यह दिखाती है कि क्यों कमांड पैटर्न (Command Pattern) जटिल मॉक्स (mocks) की तुलना में एक साफ-सुथरा और आसानी से टेस्ट किया जा सकने वाला विकल्प प्रदान करता है।
आइए एक उदाहरण देखें: इंक्रीमेंटल बिल्ड (incremental builds) को बेंचमार्क करने के लिए डिज़ाइन किया गया एक फ़ंक्शन।
func Benchmark(binary string, sourcePath string, resultPath string, numBuilds int) error {
if err := build(binary); err != nil {
return err
}
source, err := read(sourcePath)
if err != nil {
return err
}
deltas := []time.Duration{}
for i := 0; i < numBuilds; i++ {
if err := write(sourcePath, edit(source, i)); err != nil {
return err
}
start := now()
if err := build(binary); err != nil {
return err
}
delta := now().Sub(start)
deltas = append(deltas, delta)
if err := revert(sourcePath); err != nil {
return err
}
}
return write(resultPath, report(deltas))
}
यह Benchmark फ़ंक्शन कई काम करता है:
इसे सीधे टेस्ट करना कठिन है क्योंकि यह चार बाहरी निर्भरताओं पर आधारित है:
build()read() और write()now()revert()कई डेवलपर्स केवल स्वतंत्र edit और report फ़ंक्शंस को ही टेस्ट कर पाते हैं, जिससे मुख्य ऑर्केस्ट्रेशन (समन्वय) बिना टेस्ट किए रह जाता है। यहाँ बताया गया है कि हम पूरी प्रक्रिया को टेस्ट करने योग्य बनाने के लिए डिज़ाइन में कैसे सुधार कर सकते हैं।
Main में ले जाएँ
🔗कभी-कभी, आप निर्भरताओं को कॉलर (अक्सर main) के स्तर पर ऊपर धकेल सकते हैं।
func Benchmark(binary string, sourcePath string, source string, numBuilds int) (string, error) {
// ... core loop logic remains ...
// returning the report string instead of writing it
return report(deltas), nil
}
func Main() error {
// setup and initial build moved here
source, _ := read(sourcePath)
result, _ := Benchmark(binary, sourcePath, source, 3)
return write(resultPath, result)
}
निर्णय: हालांकि इससे मदद मिलती है, लेकिन यहाँ यह पर्याप्त नहीं है। Benchmark लूप को अभी भी हर चरण के दौरान बिल्ड सिस्टम, क्लॉक, फ़ाइल सिस्टम और वर्ज़न कंट्रोल के साथ बुनियादी रूप से इंटरैक्ट करने की आवश्यकता होती है।
पारंपरिक ऑब्जेक्ट-ओरिएंटेड तरीका डिपेंडेंसी इंजेक्शन (Dependency Injection) है। आप सभी बाहरी कार्यों का प्रतिनिधित्व करने वाला एक इंटरफ़ेस परिभाषित करते हैं।
type Delegate interface {
Build(binary string) error
Read(path string) (string, error)
Write(path string, content string) error
Now() time.Time
Revert(path string) error
}
type BenchmarkRunner struct {
Delegate Delegate
}
func (b *BenchmarkRunner) Run(binary string, sourcePath string, resultPath string, numBuilds int) error {
// ... implementation using b.Delegate.Build(), b.Delegate.Now(), etc ...
}
निर्णय: यह अच्छी तरह से काम करता है और आपको टेस्टिंग के लिए एक नकली (fake) डेलीगेट बदलने की अनुमति देता है। हालांकि, इसके लिए हर प्रकार के साइड इफ़ेक्ट के लिए इंटरफ़ेस और मॉक इम्प्लीमेंटेशन परिभाषित करने की आवश्यकता होती है, जिससे इंटरफ़ेस अनावश्यक रूप से बड़े और जटिल (bloat) हो सकते हैं।
हर कार्य के लिए विशिष्ट इंटरफ़ेस परिभाषित करने के बजाय, हम प्रत्येक साइड इफ़ेक्ट को एक केंद्रीय “Runner” द्वारा निष्पादित किए जाने वाले एक सामान्य कमांड (generic command) के रूप में मान सकते हैं।
सबसे पहले, परिभाषित करें कि कमांड चलाने का क्या अर्थ है:
type Runner interface {
Run(cmd Command) (Result, error)
}
इसके बाद, अपने विशिष्ट कमांड्स को सरल स्ट्रक्ट्स (या स्थिरांक) के रूप में परिभाषित करें:
type BuildCmd struct { Binary string }
type ReadCmd struct { Path string }
type WriteCmd struct { Path string; Content string }
type NowCmd struct {}
type RevertCmd struct { Path string }
अब, बेंचमार्क लॉजिक को इस तरह रीफ़ैक्टर करें कि वह उन कमांड्स को लौटाए जिन्हें वह चलाना चाहता है, या नियंत्रण किसी रनर को सौंप दे:
type BenchmarkProcess struct {
runner Runner
}
func (p *BenchmarkProcess) Execute(binary, sourcePath, resultPath string, numBuilds int) error {
_, err := p.runner.Run(BuildCmd{Binary: binary})
if err != nil { return err }
readRes, err := p.runner.Run(ReadCmd{Path: sourcePath})
if err != nil { return err }
source := readRes.(string) // Type assertion needed here
// ... rest of the loop using p.runner.Run() ...
}
इस पैटर्न की असली ताकत यूनिट टेस्ट में चमकती है। आपको हर निर्भरता के लिए जटिल मॉक इंटरफ़ेस लागू करने के बजाय केवल एक FakeRunner लिखने की आवश्यकता होती है।
टेस्ट अपेक्षित कमांड्स और मॉक किए गए उत्तरों की एक सरल सूची बन जाता है:
func TestBenchmark(t *testing.T) {
// 1. Define the script: "When you see this command, return this result"
fakeRunner := NewFakeRunner([]Step{
{Cmd: BuildCmd{"binary"}, Err: nil},
{Cmd: ReadCmd{"source_path"}, Res: "source code", Err: nil},
{Cmd: WriteCmd{"source_path", "edited code"}, Err: nil},
{Cmd: NowCmd{}, Res: time.Unix(0, 0), Err: nil},
// ... and so on ...
})
p := BenchmarkProcess{runner: fakeRunner}
err := p.Execute("binary", "source_path", "result_path", 1)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// 2. Verify the runner consumed all expected steps
if !fakeRunner.IsDone() {
t.Fatalf("did not execute all expected commands")
}
}
फ़ंक्शन कॉल्स को डेटा स्ट्रक्चर्स (Commands) में बदलकर, आप अपने बिज़नेस लॉजिक को उसके साइड इफ़ेक्ट्स से अलग (decouple) कर देते हैं।
काउए की जीवनशैली का क्यूरेटेड ऑफ़लाइन संस्करण प्राप्त करें—🍏 स्वास्थ्य, 💰 धन, ❤️ प्रेम और 🧘 अर्थ को बेहतर बनाने के लिए एक व्यावहारिक मार्गदर्शिका।
जब भी मैं बेहतर जीवन जीने पर नए निबंध, पुस्तक सारांश और व्यावहारिक गाइड प्रकाशित करूँ, तो सूचना प्राप्त करें। कोई स्पैम नहीं—कभी भी सदस्यता समाप्त करें।
क्या आपका कोई प्रश्न, दृष्टिकोण या सुझाव है? नीचे अपने विचार साझा करें। टिप्पणियाँ GitHub Discussions (विज्ञापन-मुक्त और स्पैम-मुक्त) द्वारा संचालित हैं—टिप्पणी करने या प्रतिक्रिया देने के लिए नीचे Sign in with GitHub पर क्लिक करें (निःशुल्क GitHub खाता बनाने में एक मिनट से भी कम समय लगता है)।
अंतिम अपडेट: 11 अक्टू॰ 2026.
अस्वीकरण (DISCLAIMER): इस साइट की सामग्री केवल सूचनात्मक और शैक्षिक उद्देश्यों के लिए है और यह चिकित्सा, मनोवैज्ञानिक, वित्तीय, निवेश, कर या कानूनी सलाह का गठन नहीं करती है। यहाँ प्रस्तुत विचार, संकलन और राय पूरी तरह से मेरे अपने हैं और मेरे नियोक्ता का प्रतिनिधित्व नहीं करते हैं। पुस्तकों, व्याख्यानों और लेखों के सारांश स्वतंत्र शैक्षिक टिप्पणियाँ हैं और उनके संबंधित लेखकों की बौद्धिक संपदा बने रहते हैं। संपादन, स्वरूपण और संश्लेषण में सहायता के लिए AI उपकरणों का उपयोग किया जा सकता है; यद्यपि सटीकता सुनिश्चित करने का हर संभव प्रयास किया जाता है, फिर भी त्रुटियाँ हो सकती हैं। गोपनीयता (PRIVACY): यह साइट वैकल्पिक न्यूज़लेटर सदस्यता (follow.it), टिप्पणियों (giscus / GitHub Discussions) और एम्बेडेड मीडिया (YouTube) के लिए तृतीय-पक्ष सेवाओं का उपयोग करती है, जो अपनी संबंधित गोपनीयता नीतियों के अनुसार आगंतुकों के IP पते और कुकीज़ को संसाधित कर सकती हैं।