TDD練習 Codewars_Stop gninnipS My sdroW!

這份題目的需求是針對句子中每一個超過5個(含)進行反轉,並輸出反轉後的句子。

Codewars kata題目:Stop gninnipS My sdroW! TDD練習。

繼這周遇見91看見他那高強的功力進行重構程式後,對TDD的概念有更深的感覺,所以決定在每天有時間都去他強力推薦的Codewars裡找題目練習TDD

Codewars的題目大部分相較於單純,十分的適合練習TDD。
為什麼會選這題呢? 哦..因為他剛好自己跳出來的啊  我覺得看起來很簡單就做了
因為他看起來起來很好拆解,所以我把他拆成了以下幾個需求

  • Split方法
  • 字串長度判斷方法
  • 反轉字串方法
  • 字串串接方法

首先寫一個測試,輸入空字串,我們預期結果是空字串,其Test Code如下

[TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void Input_Empty_Should_Be_Empty()
        {
            var input = string.Empty;
            var expected = string.Empty;
            var actual = Kata.spinWords(input);
            Assert.AreEqual(expected,actual);
        }
    }

一開始的Production Code如下,然後跑測試,跑完測試,出現了第一個紅燈

public class Kata
    {
        public static string spinWords(string input)
        {
            throw new NotImplementedException();
        }
    }

接著用最簡單的方式去讓他變成綠燈,Production Code 改成如下

public class Kata
    {
        public static string spinWords(string input)
        {
            return string.Empty;
        }
    }

再來我們綠燈之後,新增一個測試案例,輸入為aAAa,預期結果為aAAa

這個測試案例的目的是讓Production Code可以進行印出

其Test Code如下

public void Input_aAAa_Should_Be_aAAa()
        {
            var input = "aAAa";
            var expected = "aAAa";
            var actual = Kata.spinWords(input);
            Assert.AreEqual(expected,actual);
        }

將Production Code改成可以回傳自己

public class Kata
    {
        public static string spinWords(string input)
        {
            return input;
        }
    }

跑測試過了之後發現Test Code是可以進行重構的,於是重構了Test Code,其Code 以及 重構後的Test Code如下

 [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void Input_Empty_Should_Be_Empty()
        {
            spinResult(string.Empty,string.Empty);
        }

        [TestMethod]
        public void Input_aAAa_Should_Be_aAAa()
        {
            spinResult("aAAa", "aAAa");
        }

        private static void spinResult(string input, string expected)
        {
            var actual = Kata.spinWords(input);
            Assert.AreEqual(expected, actual);
        }
    }

重構完成了,我們就開始寫新的測試案例吧! 輸入Hello 預期結果是 olleH,其測試Code 如下,現在要新增測試案例變得十分簡便,不需要再往上copy paste 測試案例的code
所以新增的測試案例Code如下

這個測試案例的目的是實做一個針對字串反轉的方法。

public void Input_Hello_Should_Be_olleH()
        {
            spinResult("Hello","olleH");
        }

首先先讓這個測試案例失敗,紅燈了,再開始改Production Code來完成反轉字串的方法,完成後亮綠燈,改完的Code 如下 

public class Kata
    {
        public static string spinWords(string input)
        {
            if (input.Length > 4)
            {
                char[] inputArray = input.ToCharArray();
                Array.Reverse(inputArray);
                return new string(inputArray);
            }
            return input;
        }
    }

Prodcution Code 改完之後再新增一個測試案例,輸入 Hello World 預期結果為 olleH dlroW,其Test Code如下。

這個案例是直接split字串後在split後的字串進行處理,處理完成後再將它們Join起來

這邊我不小心跨太大步了,所以這邊要新增的測試應該要是只針對多個長度未超過4的字串作為測試案例(或其他)才會比較好,EX:input "this is a test. shoud" be "this is a test"

 [TestMethod]
        public void Input_Hello_World_Should_Be_olleH_dlroW()
        {
            spinResult("Hello World", "olleH dlroW");
        }

這時候要讓Production Code去跑每一個個別的字串,所以要用string的Split()方法去針對超過長度4的字串做個別處理,處理完成後讓使用Split()方法後的字串陣列去做串接[string.Join()] (其中間都要有空白),code如下

public class Kata
    {
        public static string spinWords(string input)
        {
            string[] inputArray = input.Split();
            for (int i = 0; i < inputArray.Length; i++)
            {
                char[] inputCharArray = inputArray[i].ToCharArray();
                if (inputArray[i].Length > 4)
                {
                    Array.Reverse(inputCharArray);
                }
                inputArray[i] = new string(inputCharArray);
            }
            return string.Join(" ",inputArray);
        }
    }

更改Production Code之後,我們將反轉字串的方法提取出來做並重構

public class Kata
    {
        public static string spinWords(string input)
        {
            string[] inputArray = input.Split();
            for (int i = 0; i < inputArray.Length; i++)
                inputArray[i] = reverseArray(inputArray[i].ToCharArray());
            return string.Join(" ", inputArray);
        }
        private static string reverseArray(char[] inputArray)
        {
            if (inputArray.Length > 4)
                Array.Reverse(inputArray);
            return new string(inputArray);
        }
    }

接下來補上一些codewars上的測試資料進行測試,最後所有測試案例的Code如下

    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void Input_Empty_Should_Be_Empty()
        {
            spinResult(string.Empty, string.Empty);
        }

        [TestMethod]
        public void Input_aAAa_Should_Be_aAAa()
        {
            spinResult("aAAa", "aAAa");
        }

        [TestMethod]
        public void Input_Hello_Should_Be_olleH()
        {
            spinResult("Hello", "olleH");
        }

        [TestMethod]
        public void Input_Hello_World_Should_Be_olleH_dlroW()
        {
            spinResult("Hello World", "olleH dlroW");
        }

        [TestMethod]
        public void Input_Welcome_Should_Be_emocleW()
        {
            spinResult("Welcome", "emocleW");
        }

        [TestMethod]
        public void Input_Hey_fellow_warriors_Should_Be_Hey_wollef_sroirraw()
        {
            spinResult("Hey fellow warriors","Hey wollef sroirraw");
        }

        [TestMethod]
        public void Input_This_is_a_test_Should_Be_This_is_a_test()
        {
            spinResult("This is a test","This is a test");
        }

        [TestMethod]
        public void Input_This_is_another_test_Should_Be_This_is_rehtona_test()
        {
            spinResult("This is another test", "This is rehtona test");
        }

        [TestMethod]
        public void Input_You_are_almost_to_the_last_test_Should_Be_You_are_tsomla_to_the_last_test()
        {
            spinResult("You are almost to the last test", "You are tsomla to the last test");
        }

        [TestMethod]
        public void Input_Just_kidding_there_is_still_one_more_Should_Be_Just_gniddik_ereht_is_llits_one_more()
        {
            spinResult("Just kidding there is still one more", "Just gniddik ereht is llits one more");
        }

        private static void spinResult(string input, string expected)
        {
            var actual = Kata.spinWords(input);
            Assert.AreEqual(expected, actual);
        }
    }

這一次的TDD練習,在最後那Hello World測試案例,一次時做太多了,下一次要注意思考好跨得步伐大小,這一次跨太大步就發生了一堆東西搞在一起的問題,這是不應該發生的。

在完成重構Production Code 之後 原以為這個Code 已經算是蠻精簡的了,但把這份Code上傳到Codewars後,看到別人寫出來的其他Solution就發現有更精簡,也可以結合Linq的方式去完成這份程式,這讓我感到很興奮!! 因為我完全沒想到有這種作法!

這裡是本文的commit history


91在介紹codewars以及跟我私聊幾句時說了下面這段話

我們需要找到一種持續給自己回饋的方式,才能持續改善
所以 scrum 裡面有 iteration (迭代)
所以有 retrospective
一切都是持續改善的基本要素

我想Codewars就是一個很棒的回饋方式,因為你以為你已經寫到很精簡的Code時,在提交之後就會發現一個新的世界!!!