Blog
0

Test-Driven Development: An Introduction with a JUnit Example

  • Pradyumn Sharma
  • April 11, 2017

Tags: , ,

One of the most important practices from Extreme Programming, which I cannot overemphasize, is Test Driven Development (TDD). This is a great way of achieving clarity about the requirements from a system / component / story / class, as well as verifying that the implementation meets our requirements.

In this article, I’ll talk about test-driven development at the level of a single class. Testing at this level is called unit testing, as it tests a single unit of source code. The same concepts can also be applied on a larger scale, involving an entire story, or a module, end-to-end workflow, or even an entire system. But I’ll not cover those aspects in this article.

Example: Binary Search Algorithm

Let us take an example. Suppose I want to implement the binary search algorithm. Remember binary search? Most of us learnt that in college.

Binary search is one of the techniques for searching for a value (say, an integer) inside a sorted array. A simpler, but less efficient approach is linear search, in which compare every element of the array with the search value, until we find the value or we are able to conclude that it is not present in the array.

In binary search, we first look at the middle position in the array. If we are very lucky then the search value is found right there. But even otherwise, either the value we are looking for is LESS THAN the value in the middle of the array, or it is GREATER THAN that. So in both these cases one half of the array is ruled out and we have a problem that is, in one single step, half the size of the original problem.

If you know the Big-O notation, the linear search approach is O (n), while the binary search is O (log2n), a significant improvement in efficiency.

Typical, Non-TDD Approach

Most of the times, the approach that we see in practice is somewhat like this:

  • Developer writes code.
  • He believes that the code is complete. Now time for some testing.
  • He writes some more code to accept inputs for testing at the console and display the result.
  • He runs the test:
    • Enter array values: 11, 22, 33, 44, 55
    • Enter value to search for: 22
    • Result displayed: 1 (arrays having zero-based indexes)
    • Conclusion: output is correct
  • Enter some more data for a failing scenario, such as:
    • Enter array values: 11, 22, 33, 44, 55
    • Enter value to search for: 20
    • Result displayed: -1
    • Conclusion: output is correct
  • End of developer testing. Release the code to QA.
  • QA finds a defect.
  • Developer fixes the defect. Runs a test for the defect reported. The test passes. May re-run earlier tests.
  • QA finds another defect.
  • Developer fixes this defect also. Runs a test for the defect reported. The test passes. Does not re-run earlier tests, as it getting a little too bothersome to enter the same data again and again.
  • The cycle of defect-fix-test continues for many more rounds.

The TDD Approach: An Overview

The TDD approach upends the cycle of code-test-defect-fix. In this approach, I would begin by identifying various scenarios for which my class implementing the binary search algorithm will need to be tested:

  • An array with an odd number of elements (such as 7), and searching for
    • The value at the middle position
    • The first value
    • The last value
    • Some other values in between
    • A missing value less than the first value
    • A missing value greater than the last value
    • Some other missing value
  • An array with an even number of elements (such as 8), because in such a case the middle position is ambiguous. Similar test condition as above, except that I should look for both the middle positions
  • Should I explicitly test for an array having only one element? Even though, one is an odd number, but no harm in testing for this special case. Sometimes, our programs do fail for boundary conditions, even if those are simple cases.
  • I would then prepare my test data, in order to test the various scenarios that I had identified earlier:

    Array contents

    Search value

    Expected result

    11, 22, 33, 44, 55, 66, 77

    11

    0

    44

    3

    77

    6

    33

    2

    9

    -1

    45

    -1

    100

    -1

    11, 22, 33, 44, 55, 66, 77, 88

    11

    0

    44

    3

    55

    4

    77

    6

    88

    7

    33

    2

    9

    -1

    45

    -1

    100

    -1

    11

    11

    0

    9

    -1

    45

    -1

    To my perhaps less-experienced mind, these test cases suffice. It is possible that I’ll think of some other conditions later, for which my class needs to be tested. Or a tester with a sharper mind will identify some such cases. But for the time being, the above test cases seem adequate to me.

    Automating Tests with JUnit

    Time now to automate my test infrastructure. Even before I write the first line of actual code for binary search algorithm.

    I am going to develop my class in Java, so I’ll use JUnit, an open-source framework for automating unit tests. There are similar unit testing frameworks for other programming languages (such as MSTest for .NET, CppUnit for C++, etc), but more about those some other time.

    I am going to use Eclipse as the IDE, which comes bundled with JUnit. But all other prominent IDEs for Java (such as NetBeans and JDeveloper) also support JUnit with equal ease.

    I create a new project in Eclipse. I am going to name my class (that implements the binary search algorithm) as, well, BinarySearch. But I am not going to create this class now. I am first going to create the class that will test BinarySearch.

    Creating a Test Class

    I decide that the class for implementing the binary search will be named BinarySearch (but I’ll implement it later). But first, I should have a class that will test the BinarySearch class. Following the common convention for naming JUnit test classes, I’ll call the test class BinarySearchTest. So I begin my Eclipse project by creating a new “JUnit Test Case” in an appropriate package:

    I specify the name for the test class:

    Eclipse creates the class with the default code as follows:

    1
    2
    3
    4
    5
    6
    public class BinarySearchTest {
        @Test
        public void test() {
            fail("Not yet implemented");
        }
    }

    A method with the @Test annotation is, well, a JUnit test method. Test methods are, by convention, named using the scheme test[thisbehavior], as we’ll see in a listing in a minute.

    Creating the Test Methods

    So I replace the default test method created by Eclipse with the following methods, all empty initially:

    1
    2
    3
    4
    5
    6
    public void testArrayWithOddNumberOfElements() {
    }
    public void testArrayWithEvenNumberOfElements() {
    }
    public void testArrayWithOnlyOneElement() {
    }

    I start by implementing only one test case in the testArrayWithOddNumberOfElements() method, as follows:

    1
    2
    3
    4
    5
    public void testArrayWithOddNumberOfElements() {
        int [] collection = new int []
                    {11, 22, 33, 44, 55, 66, 77};
        assertEquals (0, BinarySearch.search(collection, 11));
    }

    assertEquals is one of the methods provided by JUnit, to compare the expected and the obtained results. Its syntax is:

    assertEquals (<expectedValue>, <actualValue>)

    where <actualValue> is replaced by a call to the method that we want to test.

    As Eclipse complains about the missing class BinarySearch, I ask it to create one for me. Then, with some help from Eclipse, I create the method int search (int [] collection, int value) in the BinarySearch class.

    Starting with a Failing Implementation

    I must have some implementation in the search method that returns an int. I’ll worry about the actual implementation later, but for the time being, I write a failing implementation (an implementation that should always fail) as follows:

    1
    2
    3
    4
    5
    public class BinarySearch {
        public static int search(int[] collection, int value) {
            return -2;
        }
    }

    With this failing implementation and a single test case, I run the test

    JUnit displays a red bar. That means test failed. That was expected, of course.

    I have taken the first baby steps for building my test infrastructure. I can start some serious work now.

    Implementing the Test Methods

    I now implement all the test methods in the BinarySearchTest class, with the test data that I had already identified:

    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
    public class BinarySearchTest {
        @Test
        public void testArrayWithOddNumberOfElements() {
            int [] collection = new int []
                    {11, 22, 33, 44, 55, 66, 77};
            assertEquals (0, BinarySearch.search(collection, 11));
            assertEquals (3, BinarySearch.search(collection, 44));
            assertEquals (6, BinarySearch.search(collection, 77));
            assertEquals (2, BinarySearch.search(collection, 33));
            assertEquals (-1, BinarySearch.search(collection, 9));
            assertEquals (-1, BinarySearch.search(collection, 45));
            assertEquals (-1, BinarySearch.search(collection, 100));
        }
     
        @Test
        public void testArrayWithEvenNumberOfElements() {
            int [] collection = new int []
                    {11, 22, 33, 44, 55, 66, 77, 88};
            assertEquals (0, BinarySearch.search(collection, 11));
            assertEquals (3, BinarySearch.search(collection, 44));
            assertEquals (4, BinarySearch.search(collection, 55));
            assertEquals (6, BinarySearch.search(collection, 77));
            assertEquals (7, BinarySearch.search(collection, 88));
            assertEquals (2, BinarySearch.search(collection, 33));
            assertEquals (-1, BinarySearch.search(collection, 9));
            assertEquals (-1, BinarySearch.search(collection, 45));
            assertEquals (-1, BinarySearch.search(collection, 100));
        }
     
        @Test
        public void testArrayWithOnlyOneElement () {
            int [] collection = new int [] {11};
            assertEquals (0, BinarySearch.search(collection, 11));
            assertEquals (-1, BinarySearch.search(collection, 9));
            assertEquals (-1, BinarySearch.search(collection, 45));
        }
    }

    Just to be sure, I run the tests.

    The tests fail. All of them. Notice the small cross mark against each test method name. If the tests had not failed, I would have been shocked.

    Implementing the BinarySearch Class

    Now, I have the complete test infrastructure in place. I can finally turn my attention to implementing the BinarySearch class. I write some code. Run the tests. Some tests pass, some fail. I modify the code. Again I run all the tests. I repeat this process till all tests pass.

    Here is the code that manages to get all the tests pass:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class BinarySearch {
     
        public static int search(int[] collection, int value) {
     
            int size = collection.length;
            int lower = 0;
            int upper = size - 1;
            int middle;
             
            while (lower <= upper) {
                middle = (lower + upper) / 2;
                if (collection [middle] > value) {
                    upper = middle - 1;
                } else if (collection [middle] < value) {
                    lower = middle + 1;
                } else // we found the data
                    return middle;
            }      
            return -1;
        }
     
    }

    This is how JUnit informs me that all the tests have passed, with a green bar.

    Now I am confident of my BinarySearch class. I request a tester to help me with my test cases, and see if she can identify any other scenarios that I have missed out. If we find some such scenarios, I add more test methods or assertions to the test class.

    Trying Out Some Alternative Implementation

    I look at my code in the BinarySearch class again. It is late in the day. My mind is a little foggy. I am not sure, but I feel that perhaps the statement

    upper = middle – 1;

    ought to be

    upper = middle;

    And similarly, the statement

    lower = middle + 1;

    ought to be

    lower = middle;

    I can try these changes and see whether the program continues to work as expected. Here is the modified code:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class BinarySearch {
     
        public static int search(int[] collection, int value) {
     
            int size = collection.length;
            int lower = 0;
            int upper = size - 1;
            int middle;
             
            while (lower <= upper) {
                middle = (lower + upper) / 2;
                if (collection [middle] > value) {
                    upper = middle;
                } else if (collection [middle] < value) {
                    lower = middle;
                } else // we found the data
                    return middle;
            }      
            return -1;
        }
     
    }

    I run the code, hoping to see a green bar. Or fearing a red bar. But nothing appears. The program seems to have gone into an infinite loop. I manually terminate the program.

    Timeout as Test Failure

    When my program goes into an infinite loop, there ought to be a better way than my waiting for a long time and then terminating it. There is. I can ask JUNit to report a test as having failed if it takes longer a specified amount of time.

    For example, given the amount of data in my test methods, five seconds can be a very generous upper limit of time for these to complete. I can specify this time limit with a timeout parameter (in milliseconds) as shown in the code below:

    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
    38
    public class BinarySearchTest {
     
        @Test (timeout=5000)
        public void testArrayWithOddNumberOfElements() {
            int [] collection = new int []
                    {11, 22, 33, 44, 55, 66, 77};
            assertEquals (0, BinarySearch.search(collection, 11));
            assertEquals (3, BinarySearch.search(collection, 44));
            assertEquals (6, BinarySearch.search(collection, 77));
            assertEquals (2, BinarySearch.search(collection, 33));
            assertEquals (-1, BinarySearch.search(collection, 9));
            assertEquals (-1, BinarySearch.search(collection, 45));
            assertEquals (-1, BinarySearch.search(collection, 100));
        }
     
        @Test (timeout = 5000)
        public void testArrayWithEvenNumberOfElements() {
            int [] collection = new int []
                    {11, 22, 33, 44, 55, 66, 77, 88};
            assertEquals (0, BinarySearch.search(collection, 11));
            assertEquals (3, BinarySearch.search(collection, 44));
            assertEquals (4, BinarySearch.search(collection, 55));
            assertEquals (6, BinarySearch.search(collection, 77));
            assertEquals (7, BinarySearch.search(collection, 88));
            assertEquals (2, BinarySearch.search(collection, 33));
            assertEquals (-1, BinarySearch.search(collection, 9));
            assertEquals (-1, BinarySearch.search(collection, 45));
            assertEquals (-1, BinarySearch.search(collection, 100));
        }
     
        @Test (timeout = 5000)
        public void testArrayWithOnlyOneElement () {
            int [] collection = new int [] {11};
            assertEquals (0, BinarySearch.search(collection, 11));
            assertEquals (-1, BinarySearch.search(collection, 9));
            assertEquals (-1, BinarySearch.search(collection, 45));
        }
    }

    I run my tests again. This time JUnit reports all the test methods as having failed because of timeout.

    I think that’s cool.

    Back to Safety

    As my experiment with the modification failed, I revert the code to the earlier version, and run the tests again. This time, as expected, all the tests pass again.

    Final Words

    This is an introductory tutorial to the concepts of TDD, as well as the basics of JUnit. JUnit has many many more features to help in specifying the expectations from our code and verifying that these expectations continue to be met. For more details on JUnit, you may visit www.junit.org.

23 responses to “Test-Driven Development: An Introduction with a JUnit Example”

  1. Im obliged for the article post.Really looking forward to read more. Keep writing.

  2. Looking forward to reading more. Great blog. Fantastic.

  3. I just couldn’t go away your internet site previous to suggesting aplikasi berita saham terkini i actually liked the normal info an individual give for your company? Are going to be just as before often to be able to have a look at innovative posts

  4. “Great, thanks for sharing this post.Thanks Again. Want more.”

  5. Aerie says:

    Greetings! Very helpful advice on this article! It is the little changes that make the biggest changes. Thanks a lot for sharing!

  6. It truly is actually a nice practical little bit of info. I am pleased that you choose to contributed this useful facts along with us. Remember to continue being us all informed similar to this.. saham hari ini Thanks for giving.

  7. What i don’t realize is in fact how you are no longer actually a lot more smartly-favored than you might be now. You are so intelligent. You realize therefore considerably with regards to this matter, made me personally believe it from so many varied angles. Its like men and women are not fascinated unless it¦s one thing to do with Woman gaga! Your individual stuffs nice. Always care for it up!

  8. Incredible, great blog site structure! The span of time have you been posting with regard to? you create writing a blog look uncomplicated. The entire look of one’s website . bursa saham onlineis wonderful, seeing that neatly as the written content!

  9. It is great a chance to create a very few plans for that long run in fact it is a chance to smile. I master this kind of upload and when I’ll I wish to inform you quite a few exciting points or maybe ideas. You could possibly can easily create following reports with this document. I wish to find out more aspects of this!

  10. I am surfing around on-line greater than a couple of hours these days, nevertheless I by no means discovered any kind of exciting article similar to you. It really is attractive price sufficient in my opinion influential celebrities. Individually, if perhaps almost all internet marketers as well as writers made excellent articles as you do, online can be much more helpful than previously.

  11. Hey there cool weblog! Male. Exceptional. Astounding. Let me bookmark your website in addition to consider the provides nourishment to likewise? I’m happy to locate countless helpful tips below inside the send, we need work out excess techniques on this reverence, appreciate sharing. Hey there cool weblog! Male. Exceptional. Astounding. Let me bookmark your website in addition to consider the provides nourishment to likewise? I’m happy to locate countless helpful tips below inside the send, we need work out excess techniques on this reverence, appreciate sharing.

  12. OSTLUND says:

    Outstanding website listed here! Furthermore your website a great deal up fast! Exactly what webhost are you the utilization of? Can one get your internet marketer backlink to the number? If only . aplikasi berita saham terkinithis site crammed as promptly while the one you have lol

  13. House of Holland 太陽眼鏡系列廣告 – STYLE-TIPS.COM 人氣時尚品牌 House of Holland 今次請了來自倫敦「It girl…

  14. Their just like you go through my thoughts! Material have an understanding of a lot about it, such as you had written the particular guidebook inside it or something. I think that you just could do with a few Per-cent to be able to electrical power the solution residence a lttle bit, however besides that, that is perfect weblog.. aplikasi android prediksi saham indonesia An excellent study. I will unquestionably return to their office.

  15. my website says:

    Your blog on Test-Driven Development: An Introduction with a JUnit Example – Pragati Software is awesome. We hope you can continue delivering many more post in the future. Viva 24x7weboffers.in

  16. It is definitely a good and beneficial section of information. My business is delighted you provided this beneficial info along with us. You should stop us informed such as this.. koleksi youtube terbaik Many thanks expressing.

  17. 激光靶向祛痣:對斑痣的顯性症狀能快速控制、甚至消除,短期實現肌膚無暇;多款激光設備深層激活膠原蛋白活性,深層煥發激活生命力。快速補充肌膚活性:根據顧客肌膚特性,醫生配套無創注射項目,為肌膚注入玻尿酸、肉毒素等能直接補充肌膚活性,快速補充肌膚缺失的玻尿酸、膠原蛋白,並能達到年輕態塑型的效果,從膚質和形態上保持年輕。聯合療法:注射玻尿酸、肉毒素等產品、配合精確波長的激光能加速透明質酸、肉毒素等物質的吸收,能極大化提高注射營養物質轉換率,最高達30以上,塑形效果保持3.5個月以上,年輕態效果保持時間延長1年以上。

  18. 除皺特點 純度高,效果好 見效迅速,無創無痛 安全精準,表情自然 唯一在臨床有20多年的注射材料,全球超過11000000人使用 適用範圍 魚尾紋、額頭紋、眉間紋、鼻紋和頸部皺紋都可以祛除; 最適合於早期的、不太明顯的皺紋 不須使用任何的鎮靜劑或局部麻醉劑,且Botox除皺治療後可立即繼續進行正常的活動。 Botox除皺可以利用午休時間約診即可,完全不影響工作的安排。BOTOX肉毒桿菌素是一種高度純化的蛋白質,經過注射之後,能夠使導致動態皺紋的肌肉得到放鬆,它能夠阻斷導致肌肉收縮的神經細胞,使面部線條變得平滑並防止新皺紋形成。全球銷量第一品牌 在中國唯一獲得SFDA、FDA批准用於醫療美容的肉毒桿菌素 唯一一個擁有40年安全記錄的肉毒桿菌素 在70個國家獲得批准使用 BOTOX肉毒杆菌素

  19. 7月優惠 says:

    「 生蛇 」( 醫學名稱是 『帶狀疱疹』Shingles ) 是由水痘病毒引起的疾病。水痘癒合後,病毒在神經細胞潛伏多年,日後隨時重新激活後導致的帶狀皮疹,就是「 生蛇 」。因此,凡感染過水痘的人,都有機會「 生蛇 」。病發時,身體某一部位會感到內疼或劇痛,通常是臉或背部及腰部的一側;這時由於病毒從脊髓處沿其中華一條神經擴散到身體以及去到連接的皮膚表面,形成紅疹及小水疱。帶狀疱疹疫苗Zostavax康栢苗 (蛇針)能助減低「 生蛇 」的機會,有效預防: 「 生蛇 」( 帶狀疱疹 ) 成效高達 70% 「 生蛇 」的後遺神經痛:即使紅疹痊癒,神經痛還可以持續數月或數年。年紀越大,持續有後遺 神經痛風險越高 「 生蛇 」引起的急性及長期痛症 不適用人士: 曾對疫苗成分包括明膠( Gelatin ) 及紐奧黴 ( Neomycin ) 有過敏性休克反應的人士 有原發及繼發性免疫系統問題的病患者,或正服用抑鬱免疫系統藥物(如高劑量膽固醇)的病人 患有肺結核,正發病及未經治療人士 孕婦

  20. Venus Viva™對所有皮膚類型都是安全的,並使用革命性的Nano Fractional Radio Frequency™(納米點陣射頻™)和Smart Scan™(智能掃描™)技術,通過選擇性真皮加熱,從而提供優異的治療效果。使用Nano Fractional RF™將能量透過表皮傳遞至真皮,從而產生熱量,並啟動膚膚的生理機制,重建膠原蛋白及刺激纖維母細胞,最終刺激導致組織重塑。功效:✔改善膚質✔肌膚緊緻✔減淡妊娠紋✔痤瘡及暗瘡疤痕✔減淡細紋及皺紋✔面部肌膚賦活再生 適合面部及頸部

  21. 最新優惠 says:

    當蛋白線埋入皮膚後,皮下組織會將蛋白線視為異物,啟動異物反應,因此刺激膠原蛋白生長,且可促進新陳代謝,更新老化肌膚,所以客戶在術後也會發覺膚質變得較透亮白皙。膠原蛋白提拉線像一個“磁力線”一樣,將埋入處附近的肌肉和脂肪固定在原處,不會往下墜,其後會吸引皮下組織往蛋白線集中部位移動,就會逐漸產生提拉的緊實效果。也被用於填充美容的微整形,蛋白線材就好比蓋房子的鋼骨支架,先埋入皮膚當作基底結構,然後再注入好比水泥的玻尿酸或自體脂肪,因為有吸附力不易擴散,就可穩定固定住填充物,讓立體支撐力效果更好。

Leave a Reply

Your email address will not be published. Required fields are marked *

© 2017 Pragati Software Pvt. Ltd. All Rights Reserved.

Enquiry