Quy tắc để cải thiện test automation code
Test automation vốn cực kì khó. Cho dù là với system, integration, hay unit level, nó luôn khó nhằn từ đầu tới cuối. Bạn thậm chí đôi khi còn chẳng biết bắt đầu từ đâu. Và khi đã bắt đầu thì bạn lại chả biết cách xử lí tiếp như thế nào. Thế nên sẽ tuyệt vời hơn nếu có cách giúp ...
Test automation vốn cực kì khó. Cho dù là với system, integration, hay unit level, nó luôn khó nhằn từ đầu tới cuối. Bạn thậm chí đôi khi còn chẳng biết bắt đầu từ đâu. Và khi đã bắt đầu thì bạn lại chả biết cách xử lí tiếp như thế nào.
Thế nên sẽ tuyệt vời hơn nếu có cách giúp bạn viết ra code automation dễ dàng hơn. Tin vui là hoàn toàn có những qui tắc đấy. Sau đây là 4 điều sẽ giúp bạn cải thiện lên rất nhiều trong automation code.
Xác thực các trường hợp không đúng
Đây là một điều cực kì quan trọng để bảo đảm test automation của bạn thật sự hoạt động đúng. Nói cách khác liệu test automation của bạn bắt được bug cũng như ngăn regression trong app khi được test (AUT) hay không? Liệu automated tests của bạn có xác thực được rằng app hoạt đúng theo yêu cầu?
Thật tiếc, những người mới làm test automation sẽ rất hiếm khi xác thực việc assertion có thật sự. Thậm chí tôi còn thấy nhiều developer kinh nghiệm viết ra code không bao giờ fail. Vì thế khi viết automated test, hãy bảo đảm các điều kiện fail thật sự gợi ra những hành vi đúng như bạn mong đợi.
Để áp dụng qui luật này, bạn chỉ cần thử cho vào một breakpoint trong code của bạn trên dòng assertion. Chạy code của bạn cho tới breakpoint đó. Hãy bắt fail diễn ra trong AUT và theo dõi dòng code chứa assertion đó. Nếu nó fail đúng thì automated test của bạn thật sự đã chạy tốt.
Đừng tự lặp lại chính mình
Tác giả của Agile Manifesto, Robert C. Martin, nói “Duplication (lặp lại) chính là kẻ thù chính của một hệ thống tốt”. Tránh duplication nên là mối quan tâm chính của bạn khi viết test automation. Duplication có thể chỉ đơn giản là một vài dòng code bị lặp nhiều lần. Ví dụ như bạn có nhiều test chạy như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
[TestMethod] [Description("Validate that user is able to fill out the form successfully using valid data.")] public void Test1() { Driver = GetChromeDriver(); SampleAppPage = new SampleApplicationPage(Driver); TheTestUser = new TestUser(); TheTestUser.FirstName = "Nikolay"; TheTestUser.LastName = "BLahzah"; EmergencyContactUser = new TestUser(); EmergencyContactUser.FirstName = "Emergency First Name"; EmergencyContactUser.LastName = "Emergency Last Name"; SetGenderTypes(Gender.Female, Gender.Female); SampleAppPage.GoTo(); SampleAppPage.FillOutEmergencyContactForm(EmergencyContactUser); var ultimateQAHomePage = SampleAppPage.FillOutPrimaryContactFormAndSubmit(TheTestUser); AssertPageVisible(ultimateQAHomePage); } [TestMethod] [Description("Fake 2nd test.")] public void PretendTestNumber2() { Driver = GetChromeDriver(); SampleAppPage = new SampleApplicationPage(Driver); TheTestUser = new TestUser(); TheTestUser.FirstName = "Nikolay"; TheTestUser.LastName = "BLahzah"; EmergencyContactUser = new TestUser(); EmergencyContactUser.FirstName = "Emergency First Name"; EmergencyContactUser.LastName = "Emergency Last Name"; SampleAppPage.GoTo(); SampleAppPage.FillOutEmergencyContactForm(EmergencyContactUser); var ultimateQAHomePage = SampleAppPage.FillOutPrimaryContactFormAndSubmit(TheTestUser); AssertPageVisibleVariation2(ultimateQAHomePage); } |
Rất dễ để nhận ra có khá nhiều duplication trong những automated test này. Nếu có gì thay đổi, như loại driver mà bạn muốn, hoặc tên của class đại diện cho SampleApplicationPage, hoặc là kể cả cách bạn khởi tạo từng TestUser object, ngay lập bạn sẽ gặp vấn đề ngay. Trong ví dụ trên, bạn sẽ cần update hai nơi cho bất kì thay đổi nào. Nói cách khác nó tăng số việc bạn cần làm cũng như khả năng xảy ra lỗi.
Bất cứ khi nào bạn gặp phải vấn đề tương tự như vậy, cách giải quyết rõ ràng nhất là thay dòng code đó. Trong ví dụ này, bởi do chúng là các bước cho setup, chúng ta có thể tag phương pháp đó với [TestInitialize] attribute và để test framework call method đó trước [TestMethod].
Ví dụ:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[TestInitialize] public void SetupForEverySingleTestMethod() { Driver = GetChromeDriver(); SampleAppPage = new SampleApplicationPage(Driver); TheTestUser = new TestUser(); TheTestUser.FirstName = "Nikolay"; TheTestUser.LastName = "BLahzah"; EmergencyContactUser = new TestUser(); EmergencyContactUser.FirstName = "Emergency First Name"; EmergencyContactUser.LastName = "Emergency Last Name"; } |
Phương pháp này giờ sẽ được call trước mỗi test. Nhờ đó mà cải thiện được đáng kể code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[TestMethod] [Description("Validate that user is able to fill out the form successfully using valid data.")] public void Test1() { SetGenderTypes(Gender.Female, Gender.Female); SampleAppPage.GoTo(); SampleAppPage.FillOutEmergencyContactForm(EmergencyContactUser); var ultimateQAHomePage = SampleAppPage.FillOutPrimaryContactFormAndSubmit(TheTestUser); AssertPageVisible(ultimateQAHomePage); } [TestMethod] [Description("Fake 2nd test.")] public void PretendTestNumber2() { SampleAppPage.GoTo(); SampleAppPage.FillOutEmergencyContactForm(EmergencyContactUser); var ultimateQAHomePage = SampleAppPage.FillOutPrimaryContactFormAndSubmit(TheTestUser); AssertPageVisibleVariation2(ultimateQAHomePage); } |
Test giờ đã có kích thước nhỏ đi rất nhiều, trở nên sạch hơn cũng như ít code trùng bị lặp nhau.
Duplication xuất hiện trong class cũng cần phải được loại bỏ. Ví dụ, bạn có thể có một phương pháp để kiểm tra một trang nhất định có load hay chưa. Một trong những cách thường dùng là xem HTTP status code. Sau đó bạn sẽ muốn kiểm tra URL có chứa string liên quan tới page đó.
Nếu những điểm xác thực diễn ra trong nhiều class, bạn sẽ cần phải loại trừ các duplication. Cách dễ nhất là di chuyển những đoạn code thường tới class ba mẹ (parent) bởi chúng thường sẽ có method để thực hiện hai operations cùng lúc nhằm giúp các class đơn không bị lập code. Nếu một class đơn cần một string đặc biệt để xác thực, thì class parent có thể ép nó áp dụng một property dành riêng cho class đơn.
Việc loại trừ duplication ảnh hưởng vô cùng lớn tới sự thành công của test automation code. Hãy luôn kiểm tra và loại bỏ chúng ngay lập tức.
Hãy giữ cho các functions luôn gọn nhẹ
Nghe có vẻ không hợp lí với test automation code, nhưng đây là một qui tắt rất quan trọng. Khi bạn tạo ra những function nhỏ, ta sẽ đạt được nhiều điều khác nhau. Đầu tiên, functions sẽ dễ đặt tên hơn cũng như dễ hiểu hơn và nó sẽ hiệu quả hơn.
Hãy thử xem ví dụ sau đây của một method name:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
public static string GetLanguageInUse(string content, string expectedLanguage = "") { string language = string.Empty; content = content.Trim(); var factory = new RankedLanguageIdentifierFactory(); string directory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + @"TestDataCore14.profile.xml"; var identifier = factory.Load(directory); var languages = identifier.Identify(content); var mostCertainLanguage = languages.FirstOrDefault(); if (mostCertainLanguage.Item1.Iso639_3 == "eng") language = "English"; else if (mostCertainLanguage.Item1.Iso639_3 == "spa") language = "Spanish"; if (language != expectedLanguage && expectedLanguage != "") { var letterOverrideLanguage = ""; if (content.Length == 1 && content.All(char.IsLetter)) { letterOverrideLanguage = expectedLanguage; } var mathOverrideLanguage = OverrideMathLanguage(content, expectedLanguage); var overrideLanguage = OverrideDetectedLanguage(content, expectedLanguage); if (letterOverrideLanguage != "") language = letterOverrideLanguage; else if (mathOverrideLanguage != "") language = mathOverrideLanguage; else if (overrideLanguage != "") language = overrideLanguage; } return language; } |
Nó dùng phương thức đặt tên dựa trên nội dung và ngôn ngữ, vốn được đặt sẵn ở chế độ “no language determined” – chưa xác định ngôn ngữ. Bên trong ta sẽ thấy một chuỗi string của nội dung đã được cắt tỉa bớt (Trim()). Sau đó, sử dụng chính nội dung này, một ngôn ngữ sẽ được xác định. Cuối cùng, nếu ngôn ngữ đó không phù hợp yêu cầu của if statement, thì nó sẽ bị ghi đè bởi ngôn ngữ thay thế khác.
Vậy sự khác biệt nằm ở đâu? Hãy xem ví dụ dưới đây để thấy rõ sự mạnh mẽ từ các method nhỏ gọn:
1 2 3 4 5 6 7 8 9 10 |
private static LanguageType GetLanguageInUse(string content, LanguageType expectedLanguage = LanguageType.NoLanguageDetermined) { content = content.Trim(); var language = DetermineLanguage(content); if (language != expectedLanguage && expectedLanguage != LanguageType.NoLanguageDetermined) language = OverrideUnexpectedLanguage(content, expectedLanguage); return language; } |
Phương pháp này cực kì tinh tế. Không chỉ hoàn thành đúng theo mục đích nó được tạo ra, khi bạn nhìn vào bên trong thì bạn sẽ kiếm được đúng thứ mình cần. Đây là method được dùng để phân tích ngôn ngữ trong ví dụ và phân tích xem nó có giống tiếng anh hay không.
Việc viết function không dài quá 10 dòng sẽ cho phép code bạn dễ đọc hơn. Đặt tên cho method cũng tiện lợi bởi nó chỉ làm đúng một chức năng thôi. Và khi bạn thử nhìn vào bên trong thì sẽ không có bất cứ thứ gì “ngoài ý muốn”, mọi thứ đều hoàn hảo đúng theo ý bạn.
Chỉ viết code theo yêu cầu hiện tại
Qui tắc này ám chỉ việc bạn tránh làm quá mọi thứ lên. Quá ám ảnh vào việc điệu đà thêm thắt thường xảy ra khi developer muốn tránh việc phải đụng lại code để chỉnh sửa và nâng cấp chúng trong tương lai.
Có rất nhiều cách khiến bạn bỏ quá nhiều công sức vào test code nhưng lại không hiệu quả. Một ví dụ trong đó là cố viết code cho tất cả các tình huống có thể xảy ra trong tương lai. Tôi từng thấy một test automation engineer cố tự động hoá một page object trước cả khi cần hết mọi yếu tố. Họ sẽ tạo ra một class đại diện cho một trang HTML. Tuy vậy, thay vì chỉ đơn giản là thêm các property và method cần cho test, họ sẽ thêm gần như là mọi thứ vào trong class đó.
Đây là một sai lầm tồi tệ bởi sẽ có nhiều code bị lãng phí và không được dùng tới. Và cũng không ai chắc chắn rằng trong tương lai sẽ cần những gì. Do đó, kết quả là họ tạo ra một class khổng lồ nhưng lại có rất ít code được dùng. Bất cứ ai tương tác với đống code đó phải đi qua cả đống hổ lốn và khiến bạn tốn thời gian vô ích.
Cách tốt nhất để tránh viết code khó hiểu đó là hãy bám sát các yêu cầu được đặt ra. Nếu bạn cần để test liệu một method có return đúng string, thì cứ đơn giản viết một test cho nó. Đừng thử test cho những điều kiện ở trong tương lai mà vẫn chưa hề xác định rõ. Đừng cho thêm code nào vào system chỉ để test một cách vô ích.
Lời kết
Viết test code sẽ khó, tuy vậy nếu bạn chắc chắn rằng test fail đúng theo mục đích của nó, đừng để bị trùng lặp cũng như giữ cho function luôn gọn nhẹ và chỉ nên code test của bạn theo yêu cầu hiện tại. Nếu bạn làm đúng theo những gì tôi nói thì chất lượng sẽ được cải thiện đáng kể cũng như việc bảo trì dễ dàng hơn.
Techtalk via techbeacon