出于練習目的,我得到了一個包含多個 Persons 列的大 .csv 檔案。練習是撰寫多個 LinQ 查詢,例如只選擇女性等。例如:
var query = persons.Where(p => p.Salutation == "Ms.");
為了應用查詢,我首先將 .csv 檔案的每一行轉換為 Person 類,并將 Object 添加到包含 Persons 的串列中。
List<Person> persons = new List<Person>();
var cr = new CsvableBase.CsvReader<Person>();
var csvPeople = cr.Read("data.csv", headers = true);
foreach (var person in csvPeople)
{
persons.Add(person);
}
}
這按預期作業,我可以通過查看控制臺來查詢每個查詢。例如:
public static void GetOnlyFemale(List<Person> persons)
{
var query = persons.Where(p => p.Salutation == "Ms.");
foreach (var person in query)
{
var properties = person.GetType().GetProperties();
foreach (var property in properties)
{
Console.Write($"{property.Name}: {property.GetValue(entry)}" ",");
}
Console.WriteLine();
}
}
現在,下一個練習是為每個查詢撰寫單元測驗,我不知道如何解決這個問題。我想創建一些顯示正確結果的行并將它們與查詢的相同數量的行進行比較。但一定有更好的方法嗎?
uj5u.com熱心網友回復:
你在撰寫單元測驗時遇到問題的原因是你沒有分離你的關注點:你的類可以做的太多了。制作只有一項任務的小班。如果您不熟悉關注點分離,請考慮閱讀一些有關它的背景資訊。
將您的類分成一個代表您的存盤機制的類,在您當前的版本中是一個 CSV 檔案,以及一個對您的存盤機制進行查詢的類。
在你的存盤機制的界面中隱藏它是一個 CSV 檔案。這樣,將來您可以將存盤格式更改為 JSON 檔案、XML、資料庫,或者將來您可以從 Internet 獲取資料。事實上,對于您的查詢,資料的存盤位置和方式并不重要。您真正想知道的是,您可以為此檢索類似物件的可列舉序列。
在您的情況下,您的存盤包含一系列人員。所以你的存盤至少應該有這樣的介面:
Interface IMyDataFetcher
{
IEnumerable<Person> Persons {get;}
... // fetch other data you store in your storage
}
這種存盤通常被稱為存盤庫,在倉庫的意義上,您可以在其中存盤專案,然后可以原封不動地獲取它們。從 CSV 檔案中獲取資料的類將如下所示:
class MyCsvRepository : IMyDataFetcher // TODO: invent a proper name
{
public string FileName {get; set;}
public IEnumerable<Person> Persons
{
get {...}
}
}
在獲取中,您打開 CSV 檔案并逐行閱讀,以回傳人員。如果需要,你可以很聰明并記住讀取的行,所以下次你想要 Persons 時,你不需要再次讀取檔案,但這超出了這個問題的范圍
由于您的存盤庫類沒有很多功能,因此為此撰寫單元測驗非常容易,尤其是對未找到檔案、空檔案、只有一個人的檔案、具有除 Persons 以外的其他記錄的檔案等的測驗。
執行查詢的類與存盤分開。它只知道,您可以通過某種方式獲得一系列 Persons:
class MyPersonSelector
{
public IMyDataFetcher Storage {get; set;}
public IEnumerable<Person> Females
{
get => this.Storage.Where(person => person.Gender == Gender.Female);
}
public IEnumerable<Person> Adults
{
get => this.Storage.Where(person => person.Age > 21);
}
// etc.
}
For your unit test, you don't need the CSV file, you just make it a smart list.
For example:
Requirement 1: If the storage contains only males, property Females should return an empty sequence.
Unit test:
IEnumerable<Person> malesOnlyStorage = new List<Person>()
{
new Person() {Gender = Gender.Male, ...},
new Person() {Gender = Gender.Male, ...},
new Person() {Gender = Gender.Male, ...},
}
MyPersonSelector testObject = new MyPersonSelector
{
Storage = malesOnlyStorage,
};
IEnumerable<Person> fetchedFemales = testObject.Females;
// fetchedFemales should be empty
Assert.IsFalse(fetchedFemales.Any()); // this depends on the test suite you use
Summary
By separating your concerns, you have smaller classes, that have only one task. Smaller classes have fewer functions, and thus smaller unit tests.
By separating the storage from the queries on the storage, your software supports any kind of storage. Therefore for your unit tests you can use simple lists.
Your software will be ready for future changes: if you also need to support sequences of Persons from an XML file, or from a database, your queries will still work. If you need to add another query, your storage class won't have to change, and thus the unit test for your storage class doesn't have to change.
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/452776.html
上一篇:反應測驗庫不更新狀態
