TDD練習 Codewars_Which Are In

首先解析這份題目要得需求是甚麼

輸入兩個字串 a1 和 a2 ,輸出的字串陣列內容為 a2每一個字串陣列元素中包含a1任一個陣列元素的的字串,並且將其排序輸出,不可重複

聽起來很饒口 就看看他的範例唄

a1是要比對的字串陣列 ["arp", "live", "strong"]

a2是要被比對的字串陣列 ["lively", "alive", "harp", "sharp", "armstrong"]

以"arp"為例,他會比對到 "harp" 和 "sharp" 但是 我的輸出只能輸出一個 "arp" 這樣

以上的範例可以方便我來拆解這一次的需求

  • Contains方法 用來找尋包含的字串元素
  • 比對在輸出結果有沒有重複的字串 一樣是用 Contains
  • 最後的排序 OrderBy

起手就先以a1和a2各1個元素的比對來做測試案例起手,a1的元素是arp,a2的元素是aaa

程式碼如下 我預期的結果要是空的字串陣列

[TestMethod]
public void Input_arpAnd_aaa_Should_Be_EmptyArray()
{
var actual = Kata.inArray(new string[] {"arp"}, new string[] {"aaa"});
var expected = new string[] { };
Assert.AreEqual(expected,actual);
}

而Production Code 則生成了下面這個樣子

public static string[] inArray(string[] array1, string[] array2)
{
 return new string[]{};
}

很理所當然的 這一個測試案例亮了第一次的紅燈

接下來用最簡單的方式讓紅燈變成綠燈,直接Return一個空的字串陣列XDDD

然後把剛才的筆誤Assert.AreEqual更改成 CollectionAssert.AreEqual 因為是比較陣列的關係所以要使用 CollectionAssert.AreEqual

再來新增一個會發生錯誤且Prodction Code 更改幅度為最小的測試案例,那就是會回傳正確答案的字串了

[TestMethod]
public void Input_arpAnd_aaa_Should_Be_EmptyArray()
{
var actual = Kata.inArray(new string[] {"arp"}, new string[] {"arp"});
var expected = new string[] {"arp" };
CollectionAssert.AreEqual(expected,actual);
}

很正常的,這一個測試案例會亮你紅燈,就將程式碼更改為

public static string[] inArray(string[] array1, string[] array2)
{
List<string> result = new List<string>();
foreach (var s in array2)
{ 
 if (s.Equals(array1[0]) )
 {
   result.Add(s);
 }
}
return result.ToArray();
}

直接宣告一個result字串陣列變數,方便我直接用add的方法就在foreach內新增一個字元,不用去標記index為多少
然後只用一個Foreach來完成這一次的需求,並且判斷有沒有包含(這裡的Equal當初寫錯,是後來才發現寫錯的),雖然把包含方法誤寫成Equal但還是通過了測試案例,亮了綠燈

接下來新增第三個測試案例,一樣是以可能最小步的方式來思考測試案例的方向,第三個測試案例就這樣產生了

[TestMethod]
public void Input_arpAnd_aaa_Should_Be_EmptyArray()
{
var actual = Kata.inArray(new string[] {"ss", "arp"}, new string[] {"arp"});
var expected = new string[] {"arp"};
CollectionAssert.AreEqual(expected,actual);
}

因為上一個測試案例所更改的 Production Code 只有找第一個 a1 元素來做比對,這一次就故意把他新增成兩個,很理所當然的,這一個測試案例也產生了紅燈的結果

所以在 Production Code 加上了第二個 Foreach 迴圈,這個迴圈以目前的測資來說 也會是沒有問題的,所以也亮了綠燈

public static string[] inArray(string[] array1, string[] array2)
{
 List<string> result = new List<string>();
 foreach (var a2 in array2)
 {
     foreach (var a1 in array1)
     {
         if (a2.Equals(a1))
         {
             result.Add(a2);
         }
     }
 }
 return result.ToArray();
}

亮了綠燈之後要檢視程式碼

這時候就發現不應該使用 Equals 而是要使用Contains 、 result add的不應該是 a2 而要是 a1、上層的迴圈應該要是array1 下層的則是array2

就把Production Code改成以下的樣子

public static string[] inArray(string[] array1, string[] array2)
{
var result = new List<string>();
foreach (var a1 in array1)
    foreach (var a2 in array2)
        if (a2.Contains(a1))
            result.Add(a1);

 return result.ToArray();
}

接下來再新增一個測試案例,這個測試案例是為了避免重複的結果,所以測試案例中的a2有多一個包含arp的字串

[TestMethod]
public void Input_ss_arp_And_art_starpoint_garp_Should_Be_arp()
{
    var actual = Kata.inArray(new string[] { "ss", "arp" }, new string[] { "art", "starpoint","garp" });
    var expected = new string[] { "arp" };
    CollectionAssert.AreEqual(expected, actual);
}

一樣這一個測試案例使我的專案亮了紅燈

接下來就把Production Code更改成下面這個樣子 讓他再新增元素時除了判斷有無包含之外,還判斷原有的字串陣列中有沒有包含一樣的元素,如此一來就可以把這個測試案例亮成綠燈

public static string[] inArray(string[] array1, string[] array2)
{
var result = new List<string>();
foreach (var a1 in array1)
    foreach (var a2 in array2)
        if (a2.Contains(a1) && !result.Contains(a1))
            result.Add(a1);

 return result.ToArray();
}

再來就是最後一個需求,排序的部分,所以新增了一個需要排序才能成功的測試案例,其測試案例如下

[TestMethod]
public void Input_bc_abc_And_bbbc_dsssaabcasd_Should_Be_arp()
{
    var actual = Kata.inArray(new string[] { "bc","abc" }, new string[] { "bbbc","dsssaabcasd"});
    var expected = new string[] { "abc","bc" };
    CollectionAssert.AreEqual(expected, actual);
}

這個測試案例我故意把 a 開頭的比較字串放在 a1 的第二個元素 b開頭擺在第一個,如此一來我的測試案例就會亮紅燈 

接下來只需要在我返回字串陣列的時候用Linq排序就可以完成這個需求了,也因此亮了綠燈

public static string[] inArray(string[] array1, string[] array2)
{
    var result = new List<string>();
    foreach (var a1 in array1)
        foreach (var a2 in array2)
            if (a2.Contains(a1) && !result.Contains(a1))
                result.Add(a1);
    return result.OrderBy(x => x).ToArray();
}

這一次的所有測試案例如下

    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void Input_arpAnd_aaa_Should_Be_EmptyArray()
        {
            var actual = Kata.inArray(new string[] { "arp" }, new string[] { "aaa" });
            var expected = new string[] { };
            CollectionAssert.AreEqual(expected, actual);
        }

        [TestMethod]
        public void Input_arpAndarp_Should_Be_arpOfArray()
        {
            var actual = Kata.inArray(new string[] { "arp" }, new string[] { "arp" });
            var expected = new string[] { "arp" };
            CollectionAssert.AreEqual(expected, actual);
        }

        [TestMethod]
        public void Input_ss_arpAndarp_Should_Be_arpOfArray()
        {
            var actual = Kata.inArray(new string[] { "ss", "arp" }, new string[] { "arp" });
            var expected = new string[] { "arp" };
            CollectionAssert.AreEqual(expected, actual);
        }

        [TestMethod]
        public void Input_ss_arp_And_art_starpoint_Should_Be_arp()
        {
            var actual = Kata.inArray(new string[] { "ss", "arp" }, new string[] { "art", "starpoint" });
            var expected = new string[] { "arp" };
            CollectionAssert.AreEqual(expected, actual);
        }

        [TestMethod]
        public void Input_ss_arp_And_art_starpoint_garp_Should_Be_arp()
        {
            var actual = Kata.inArray(new string[] { "ss", "arp" }, new string[] { "art", "starpoint","garp" });
            var expected = new string[] { "arp" };
            CollectionAssert.AreEqual(expected, actual);
        }

        [TestMethod]
        public void Input_bc_abc_And_bbbc_dsssaabcasd_Should_Be_arp()
        {
            var actual = Kata.inArray(new string[] { "bc","abc" }, new string[] { "bbbc","dsssaabcasd"});
            var expected = new string[] { "abc","bc" };
            CollectionAssert.AreEqual(expected, actual);
        }

        [TestMethod]
        public void CodewarsExampleTest()
        {
            var actual = Kata.inArray(new string[] { "arp", "live", "strong" },
                new string[] { "lively", "alive", "harp", "sharp", "armstrong" });
            var expected = new string[] { "arp", "live", "strong" };
            CollectionAssert.AreEqual(expected, actual);
        }
    }

接下來就把程式提交,就可以看到美妙的綠燈亮出來啦

不過最重要的是在提交之後可以看見別人所寫的程式碼,其中有一個人寫的程式碼跟我的邏輯相同但他們以Linq的方式寫出來,也更精簡,如下

心得:

在這次的練習過稱中儘管不小心把Contains方法寫成Equal 也把 加入的字串a1 寫成 a2 還有 迴圈比較也寫反,但也因為後面的測試案例修改以及每一次的重新審視Production Code而及早發現程式碼發生的問題,所以覺得除了自己的測試案例還有進步的空間之外還有自己在寫Code的時候也要更加謹慎才能更有效率地把程式完成。


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

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

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