C# 那些单元测试可以吗?
我试图掌握测试驱动的开发,我想知道那些单元测试是否合适。我有这样一个界面:C# 那些单元测试可以吗?,c#,.net,unit-testing,tdd,mocking,C#,.net,Unit Testing,Tdd,Mocking,我试图掌握测试驱动的开发,我想知道那些单元测试是否合适。我有这样一个界面: public interface IEntryRepository { IEnumerable<Entry> FetchAll(); Entry Fetch(int id); void Add(Entry entry); void Delete(Entry entry); } 谢谢 总体上看起来不错。您应该使用事务(或在TestInitialize中创建存储库的新实例)来确保
public interface IEntryRepository
{
IEnumerable<Entry> FetchAll();
Entry Fetch(int id);
void Add(Entry entry);
void Delete(Entry entry);
}
谢谢 总体上看起来不错。您应该使用事务(或在TestInitialize中创建存储库的新实例)来确保测试是真正隔离的
也可以使用更具描述性的测试方法,比如当一个新的实体被添加到一个入口存储库中时,对象的总数应该增加,在我回答之前,让我声明我对单元测试是相当陌生的,绝对不是专家,所以对我所说的一切都要三缄其口 但我觉得您的单元测试在很大程度上是多余的。许多方法都是简单的pass-through,比如AddEntry方法只是对底层列表方法Add的调用。您没有测试代码,而是测试Java库
我只推荐包含您编写的逻辑的单元测试方法。避免测试诸如getter和setter之类的明显方法,因为它们在最基本的级别上运行。这是我的哲学,但我知道有些人确实相信测试显而易见的方法,我只是碰巧认为这是毫无意义的。像这样似乎很好。我个人喜欢给我的测试起一个更具描述性的名字,但这更多的是个人偏好 您可以对正在测试的类的依赖项使用模拟,EntryRepository是正在测试的类,因此无需对其进行模拟,否则您将测试模拟实现而不是类
举个简单的例子。如果EntryRepository使用后端数据库而不是列表来存储条目,则可以为数据访问内容注入模拟实现,而不是调用真正的数据库。这看起来是一个良好的开端,但您应该尽可能多地测试“边界”情况。考虑一下什么可能会导致您的方法失败-传递一个空条目来添加或删除是否有效?尝试编写测试,测试每种可能的代码路径。如果您对代码进行任何更改,以这种方式编写测试将使您的测试套件在将来更加有用
此外,对于每个测试方法来说,让测试对象保持与调用时相同的状态也是很有用的。我注意到您的TestFetchEntry方法向EntryRepository添加了一个元素,但从未删除它。让每个方法都不影响测试对象状态,可以更容易地运行一系列测试。您不应该模拟IEntryRepository,因为实现类就是被测试的类。可能希望模拟
列表
并将其注入,然后测试通过公共接口调用的方法是否被适当调用。这只是实现它的方法的一种替代方法,并不一定更好——除非您希望注入类的支持类,在这种情况下,以这种方式编写测试将强制执行该行为
您可能需要进行更多的测试,以确保在插入条目时插入了正确的条目。与delete类似——插入两个条目,然后删除一个条目,并确保删除了正确的条目。一旦你想出了测试让代码按照你想要的去做,就要不断思考你可能会把编写代码和编写测试搞得一团糟的方法,以确保这些不会发生。假设这个类非常简单,您可以说服自己,您所做的驱动行为测试已经足够了。不过,它不需要太多的复杂性,就值得测试边缘案例和意外行为。对于TDD初学者和这个特定的类,您的测试很好+谢谢你的努力
当您遇到涉及依赖项注入和模拟的更复杂场景时,请发布另一个问题。这就是事情变得真正有趣的地方;) 就在我的头顶上 虽然您对add的测试实际上只测试框架:
- 你已经添加了1项,这很好
- 增加很多项目怎么样 (我的意思是,荒谬的数量-容器添加失败的n个条目的值是多少?)
- 不添加项目怎么样?(空项)
- 如果将项目添加到列表中,它们是否按特定顺序排列? 应该是吗
- 如果x>rep.Count,在获取(x)时会发生什么
- 如果x<0会发生什么
- 如果代表为空,会发生什么
- x是否符合性能要求(算法是什么 复杂性?当只有一个条目且 有大量的条目
- 结果正确吗
- 所有的边界条件都正确吗
- 符合预期的格式
- 顺序正确
- 在合理范围内
- 它引用任何外部依赖项吗
- 基数是否正确?(正确的值数)
- 它是否在正确的时间内完成(真实的或相对的)
- 你能检查反向关系吗
- 你能用另一种经过验证的方法交叉检查结果吗
- 你能强制错误条件吗
- 性能特征是否在范围内
- 你在进行单元测试
- 你在遵循惯例安排、行动、主张
- 当没有条目时,删除条目的测试在哪里
- 当没有条目时,获取条目的测试在哪里
- 当您添加两个条目并删除一个条目时,会发生什么情况?应该保留哪一个条目
应该是公共的。您的一个断言调用了Entries
这一事实向我表明您没有构造类corrrep.Entries.SingleOrDefault
public class EntryRepository : IEntryRepository { public List<Entry> Entries {get; set; } public EntryRepository() { Entries = new List<Entry>(); } public IEnumerable<Entry> FetchAll() { throw new NotImplementedException(); } public Entry Fetch(int id) { return Entries.SingleOrDefault(e => e.ID == id); } public void Add(Entry entry) { Entries.Add(entry); } public void Delete(Entry entry) { Entries.Remove(entry); } }
[TestClass] public class EntryRepositoryTests { private EntryRepository rep; public EntryRepositoryTests() { rep = new EntryRepository(); } [TestMethod] public void TestAddEntry() { Entry e = new Entry { ID = 1, Date = DateTime.Now, Task = "Testing" }; rep.Add(e); Assert.AreEqual(1, rep.Entries.Count, "Add entry failed"); } [TestMethod] public void TestRemoveEntry() { Entry e = new Entry { ID = 1, Date = DateTime.Now, Task = "Testing" }; rep.Add(e); rep.Delete(e); Assert.AreEqual(null, rep.Entries.SingleOrDefault(i => i.ID == 1), "Delete entry failed"); } [TestMethod] public void TestFetchEntry() { Entry e = new Entry { ID = 2, Date = DateTime.Now, Task = "Testing" }; rep.Add(e); Assert.AreEqual(2, rep.Fetch(2).ID, "Fetch entry failed"); } }
- 以下是一些想法:
阳性