我已经创建了一个购物篮,我需要为它进行单元测试。它们一直很好,直到我遇到一个小问题,我已经完成了单元测试,以便在篮子中添加一些东西等等,但是有一个选项可以将lst_Results中的所有内容保存到文本文件中您选择的文件夹中。我的保存按钮的代码是:
private void btn_Save_Click(object sender, EventArgs e)
{
var FileSave = new SaveFileDialog();
FileSave.Filter = "Text (*.txt)|*.txt";
if (FileSave.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
using (var streamwriter = new StreamWriter(FileSave.FileName, false))
foreach (var item in lst_Results.Items)
streamwriter.Write(item.ToString() + Environment.NewLine);
MessageBox.Show("Success");
}
}
然后对于实际的单元测试本身这是我到目前为止,但我不确定目标和断言应该是什么
[TestMethod()]
public void SaveItems()
{
Basket.Basket target = new Basket.Basket();
string itemname = "Orange";
int quantity = 5;
decimal price = 5;
target.AddProduct(itemname, quantity, price);
string itemname2 = "Banana";
int quantity2 = 5;
decimal price2 = 1;
target.AddProduct(itemname2, quantity2, price2);
target.???();
Assert.???);
}
答案 0 :(得分:2)
(重新)考虑你想要测试的内容。您想测试UI还是测试将数据写入流中?
测试按钮是集成测试的一部分,在单元测试中并非易事。
我建议首先将流写入自己的方法
internal static void WriteToStream(IEnumerable<Basket.Basket> items,string filename){
using (var streamwriter = new StreamWriter(filename, false))
foreach (var item in items){
streamwriter.Write(item.ToString() + Environment.NewLine);
}
}
}
private void btn_Save_Click(object sender, EventArgs e)
{
var FileSave = new SaveFileDialog();
FileSave.Filter = "Text (*.txt)|*.txt";
if (FileSave.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
WriteToStream(lst_Results.Items, FileSave.FileName);
MessageBox.Show("Success");
}
}
(标记为内部的方法允许从持有测试的外部程序集进行访问)
现在您已经隔离了流部分。这可以通过使用临时文件进行测试,并在写入后通过回读文件的内容与您期望的内容匹配来断言。
这还不是最佳选择。你现在依赖于文件系统,对于小的读/写,这不会妨碍很多。但是你可以添加另一个间接来改进测试。
考虑进一步重写(实际重构)WriteToStream()
internal static void WriteToStream(IEnumerable<Basket.Basket> items, Func<TextWriter> createStream){
using (var streamwriter = createStream())
foreach (var item in items){
streamwriter.Write(item.ToString() + Environment.NewLine);
}
}
}
private void btn_Save_Click(object sender, EventArgs e)
{
var FileSave = new SaveFileDialog();
FileSave.Filter = "Text (*.txt)|*.txt";
if (FileSave.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
WriteToStream(lst_Results.Items,() => new StreamWriter(FileSave.FileName, false));
MessageBox.Show("Success");
}
}
}
现在你可以创建一个伪造的(模拟的)StreamWriter,你可以在其上声明项目被写入 a 编写器:你只需要确保将数据写入编写器,如何以及何时作者得到它到磁盘不是你的问题。
class FakeStreamWriter : TextWriter{ //StreamWriter IS A TextWriter
public bool HasWritten {get; private set;}
public override void Write(string content){
HasWritten = true;
}
//omitted other methods for brevity
}
[Fact]
public void ItemAreWrittenToStream(){
var myFakedStreamWriter = new FakeStreamWriter();
var items = new List<Basket.Basket>{ new Basket.Basket{...}}
FormX.WriteToStream(item, () => myFakedStreamWriter );
Assert.True( myFakedStreamWriter.HasWritten );
}
答案 1 :(得分:0)
保存(如果有的话,加载)代码可能应该转移到Basket上的方法中。 btn_Save_Click中打开流并写出数据的代码将进入购物篮的保存方法。然后你的测试代码可以调用target.Save(“filename.ext”);然后,您的断言应该测试创建的文件是否符合您的预期。
如果您想要更复杂,并且避免必须执行磁盘I / O(这在某些自动化测试环境中可能很重要),那么您可以使您的保存代码接受流作为目标,然后在您的测试代码中提供内存流而不是文件流。然后,测试代码可以检查内存流并一起绕过文件I / O.
在编写测试时,我个人认为最难的事情就是确保你正在测试正确的东西。例如,你真的不需要测试FileStream并刷新到磁盘工作,因为那不是你的代码。你的代码是什么,是写入流本身的东西,这就是你想要测试的东西。这就是测试在测试保存时提供内存流的原因,即使生产设置中的代码永远不会使用内存流。
这里另一个有用的教训是,编写测试可以使您找到可能是OOP设计问题的内容。通常,具有良好OOP主体的对象非常容易测试,如果不是,那么可能是您的对象设计需要工作的线索。在这种情况下,将流写入代码移动到自己的方法中。
答案 2 :(得分:0)
您的按钮点击事件处理程序的功能超出了预期。这使得单元测试变得困难。我将按如下方式进行:
从按钮单击事件处理程序中提取保存部分。例如,您将拥有这样的方法:
public void SaveItemsTo(String fileName, IEnumerable items){
using (var streamwriter = new StreamWriter(fileName, false))
foreach (var item in items){
streamwriter.Write(item.ToString() + Environment.NewLine);
}
}
完成后,您可以轻松地测试从篮子中保存的物品。您的单元测试只缺少保存行(您现在可以通过调用提取的方法执行此操作)并断言文件已创建(您可以选择检查文件内容)。
YourClass.SaveItemsTo(yourfilename,Basket.Products);
Assert.IsTrue(File.Exists(yourfilename));
这显然只是草稿给你的想法。显然,有很多可能的改进。你把方法保存在哪里完全取决于你。