[৬.২] টেস্টিং (Testing)
[৬.২.১] টেস্টিং (Testing) কি?
টেস্টিং সফটওয়্যার ডেভেলপমেন্টের একটি অপরিহার্য অংশ। আপনার লেখা কোডটি প্রত্যাশিতভাবে কাজ করছে কিনা, কোডে করা যেকোনো পরিবর্তনের কারনে নতুন বাগ দেখা দিলো কিনা এটি নিশ্চিত করতে টেস্টিং এর বিকল্প নেই । Go তে টেস্টিং এর জন্য রয়েছে বিল্ট ইন সাপোর্ট, যা ডেভেলপারদের তাদের কোডের টেস্টিং করার ব্যাপারটিকে খুব সহজ করে দেয়।
Go এর টেস্টিং প্যাকেজ এর সাহায্যে আমরা সহজেই ডেভেলপমেন্ট চলাকালীন সময়ে ইউনিট টেস্টিং এবং বেঞ্চমার্কিং করতে পারি। Go build কমান্ড এর মত, আপনি যে টেস্ট কোডটি লিখছেন তা কার্যকর করার জন্য একটি Go test কমান্ড রয়েছে।
বিভিন্ন ধরনের টেস্টিং রয়েছে, যার মধ্যে রয়েছে:
- ইউনিট টেস্টিং
- ইন্ট্রিগেশন টেস্টিং
- ফাংশনাল টেস্টিং
- এন্ড টু এন্ড টেস্টিং
- পারফরমেন্স টেস্টিং
- সিকিউরিটি টেস্টিং
ইত্যাদি আরো অনেক ধরনের টেস্টিং থাকলেও Go -তে শুধুমাত্র বিল্ট ইন টেস্টিং প্যাকেজ ব্যবহার করে ইউনিট টেস্টিং করা যায়।
আমরা এ অধ্যায়ে শুধু ইউনিট টেস্টিং নিয়ে আলোচনা করবো।
[৬.২.২] ইউনিট টেস্টিং (Unit Testing)
ইউনিট টেস্টিং হল একটি ফাংশন যা একটি প্যাকেজ বা প্রোগ্রাম থেকে কোডের একটি নির্দিষ্ট অংশ বা সেটকে টেস্ট করে। টেস্টিং এর কাজ হল ঐ নির্ধারিত কোডটি প্রত্যাশিতভাবে কাজ করছে কিনা তা যাচাই করা।
এই সেকশনে, আমরা বিল্ট-ইন টেস্টিং প্যাকেজ ব্যবহার করে Go তে ইউনিট টেস্টিং কীভাবে লিখতে হয় তা শিখবো।
Go তে ইউনিট টেস্টিং লেখার বিভিন্ন উপায় রয়েছে। বেসিক টেস্টিং কোডের একটি নির্দিষ্ট অংশকে, একটি নির্দিষ্ট সংখ্যক প্যারামিটার এবং ফলাফলের জন্য টেস্ট করে । টেবিল টেস্টিংও কোডের একটি নির্দিষ্ট অংশ টেস্ট করে কিন্তু সেটা একাধিক প্যারামিটার এবং ফলাফলের জন্য ।
চলুন আমরা কিছু উদাহরন এর সাহায্যে দেখি Go তে টেস্টিং কিভাবে কাজ করে।। একটি ফাংশন তৈরি করুন, DivisibleByThree যা ইনপুট হিসাবে একটি ইন্টিজার নেয় এবং একটি string প্রদান করে। যদি ইনপুট সংখ্যাটি তিন দ্বারা বিভাজ্য হয়, তাহলে “Hurray!” ফেরত দিবে; অন্যথায়, একটি string হিসাবে সংখ্যাটি ফেরত দিবে।
TestingDemo.go –
package main
import "strconv"
// If the number is divisible by 3, write "Hurray! " otherwise, the number
func DivisibleByThree(input int) string {
isDivisibleByThree := (input % 3) == 0
if isDivisibleByThree {
return "Hurray!"
}
return strconv.Itoa(input)
}
Go-তে, একটি টেস্ট ফাংশন লিখতে সর্বদা নিম্নলিখিত সিগনেচার ব্যবহার করতে হবে –
func TestXyz(*testing.T)
একটি টেস্ট এর নাম সর্বদা Test দিয়ে শুরু হয়, তারপরে থাকে টেস্ট করতে চাওয়া ফাংশনের নাম, Xyz । এটি একটি আরগুমেন্ট নেয়, যা হচ্ছে testing.T টাইপের একটি পয়েন্টার (*testing.T)
Go-তে টেস্ট ফাইলের নামকরণের নিয়ম হল ফাইলের নামটি _test.go দিয়ে শেষ করা এবং ফাইলটি যে কোডটি টেস্ট করে সেই একই ফোল্ডারে স্থাপন করা। উপরের উদাহরণে, DivisibleByThree ফাংশনটি রয়েছে TestingDemo.go ফাইলে আর ফাংশনটির ইউনিট টেস্টটি রয়েছে TestingDemo_test.go ফাইলে। আর এই দুটি ফাইল রয়েছে একই ডিরেক্টোরিতে।
TestingDemo_test.go ফাইল:-
package main
import "testing"
func TestDivisibleByThree(t *testing.T) {
got := DivisibleByThree(9)
want := "Hurray!"
if want != got {
t.Errorf("Expected '%s', but got '%s'", want, got)
}
}
আমাদের উদাহরণে, TestDivisibleByThree() ফাংশনের ভিতরে got ভ্যারিয়েবলটি DivisibleByThree(9) ফাংশন কলের ফলাফলের জন্য বরাদ্দ করা হয়েছে। want ভ্যারিয়েবলটিতে প্রত্যাশিত ফলাফল “Hurray!” রাখা হয়েছে । টেস্ট এর শেষ অংশ চেক করে যে, want এবং got এর মান সমান কিনা। যদি না হয়, Errorf() মেথডটি এক্সিকিউট হয় এবং টেস্টটি ব্যর্থ হয়।
এখন, টার্মিনালে টেস্টটি রান করার জন্য go test কমান্ডটি ব্যবহার করুন।
Go test কমান্ড কারেন্ট ডিরেক্টরিতে পাওয়া সোর্স, ফাইল এবং টেস্টগুলো কম্পাইল করে, তারপর ফলাফল হিসেবে পাওয়া টেস্ট বাইনারি ফাইলকে রান করে। যখন টেস্টটি শেষ হয় তখন টেস্টটির সারাংশ হিসেবে PASS বা FAIL, কনসোলে প্রিন্ট হয়, যেমনটি নীচের কোড ব্লকে দেখা যাচ্ছেঃ
> go test
PASS
ok _/D_/GoLang/Gobyexample 0.262s
আমরা চাইলে কোনো প্যাকেজের রিলেটিভ পাথ পাস করে ঐ নির্দিষ্ট প্যাকেজের টেস্ট করতে পারি, উদাহরণস্বরূপ, go test ./package–name । এছাড়াও , আমরা কোডবেসের সমস্ত প্যাকেজের জন্য পরীক্ষা চালানোর জন্য go test ./… ব্যবহার করতে পারি।
যদি টেস্ট কমান্ডটির সাথে -v ফ্ল্যাগ যুক্ত করি, তাহলে টেস্টটি সব এক্সিকিউট হওয়া ফাংশনের নাম এবং তাদের এক্সিকিউশনের জন্য ব্যয় করা সময় প্রিন্ট করবে –
> go test -v
=== RUN TestDivisibleByThree
--- PASS: TestDivisibleByThree (0.00s)
PASS
ok _/D_/GoLang/Gobyexample 0.251s
উপরের টেস্ট উদাহরণে শুধুমাত্র একটি কেস রয়েছে। যাইহোক, নরমালি একটি টেস্টে একাধিক টেস্ট কেস থাকে, যেটা নিশ্চিত করে যে কোডের প্রতিটি ইউনিট বিভিন্ন মানের বিপরীতে সঠিক ভাবে পরীক্ষিত হয়েছে।
Go-তে, টেবিল ড্রিভেন টেস্ট এর সাহায্যে আমরা সহজেই একাধিক টেস্ট কেস এর জন্য একটি নির্দিষ্ট ফাংশন বা কোডকে টেস্ট করতে পারি, এক্ষেত্রে আমাদের সমস্ত টেস্ট কেসগুলোকে একটি স্লাইসে রেখে, তারপর প্রতিটি কেস এর সাথে প্রত্যাশিত ফলাফল তুলনা করে টেস্টটি সফল বা ব্যর্থ হয়েছে কিনা তা নির্ধারণ করা হয়।
উদাহরণস্বরূপঃ
আমরা আমাদের টেস্ট ফাংশনে আলাদা চারটি টেস্ট কেস যুক্ত করেছি যার মধ্যে ৩য় কেসটি আমাদের লজিক অনুযায়ী FAIL করবে –
package main
import "testing"
func TestDivisibleByThree(t *testing.T) {
type args struct {
input int
}
tests := []struct {
name string
args args
want string
}{
{"TestCase1 for number 9",args{9},"Hurray!"},
{"TestCase2 for number 4",args{4},"4"},
{"TestCase3 for number 13",args{13},"Hurray!"},
{"TestCase4 for number 15",args{15},"Hurray!"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if Got := DivisibleByThree(tt.args.input); Got != tt.want {
t.Errorf("DivisibleByThree() = %v, want %v", Got, tt.want)
}
}
}
কোডটি আবার রান করলে আমরা নিচের আউটপুট দেখতে পাবো। -cover এর মাধ্যমে আমরা টেস্টকৃত ফাংশনটার কতো শতাংশ কোড রান টাইমে এক্সিকিউট হয়েছে তা জানতে পারি –
> go test -v -cover
=== RUN TestDivisibleByThree
=== RUN TestDivisibleByThree/TestCase1_for_number_9
=== RUN TestDivisibleByThree/TestCase2_for_number_4
=== RUN TestDivisibleByThree/TestCase3_for_number_13
TestingDemo_test.go:26: DivisibleByThree() = 13, want Hurray!
=== RUN TestDivisibleByThree/TestCase4_for_number_15
--- FAIL: TestDivisibleByThree (0.00s)
--- PASS: TestDivisibleByThree/TestCase1_for_number_9 (0.00s)
--- PASS: TestDivisibleByThree/TestCase2_for_number_4 (0.00s)
--- FAIL: TestDivisibleByThree/TestCase3_for_number_13 (0.00s)
--- PASS: TestDivisibleByThree/TestCase4_for_number_15 (0.00s)
FAIL
_/D_/GoLang/Gobyexample coverage: 100.0% of statements
exit status 1
FAIL _/D_/GoLang/Gobyexample 0.308s
টেস্টিং আমাদের কোডে লজিক্যাল ত্রুটি ধরতে এবং আমাদের আরও নির্ভরযোগ্য এবং বাগ-মুক্ত সফ্টওয়্যার তৈরিতে সাহায্য করে। বিস্তৃত টেস্ট লিখে এবং নিয়মিত টেস্টিং চালানোর মাধ্যমে, আমরা ডেভেলপমেন্টের প্রথম দিকেই বাগগুলো ধরতে পারি যাতে পরবর্তীতে বাগগুলো আরো বড় ধরনের সমস্যা সৃষ্টি করতে না পারে।
টেস্টিং নিয়ে আরো বিস্তারিত জানতে রেফারেন্সে দেওয়া বই এবং ব্লগ গুলো কাজে আসবে।