Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/321.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 那些单元测试可以吗?_C#_.net_Unit Testing_Tdd_Mocking - Fatal编程技术网

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
        应该是公共的。您的一个断言调用了
        rep.Entries.SingleOrDefault
        这一事实向我表明您没有构造类corr
        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");
            }
        }