Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/shell/5.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_Validation_Tdd - Fatal编程技术网

C# 单元测试和验证逻辑

C# 单元测试和验证逻辑,c#,.net,unit-testing,validation,tdd,C#,.net,Unit Testing,Validation,Tdd,我目前正在为包含验证例程的业务逻辑类编写一些单元测试。例如: public User CreateUser(string username, string password, UserDetails details) { ValidateUserDetails(details); ValidateUsername(username); ValidatePassword(password); // create and return user } 我的测试夹具是

我目前正在为包含验证例程的业务逻辑类编写一些单元测试。例如:

public User CreateUser(string username, string password, UserDetails details)
{
    ValidateUserDetails(details);
    ValidateUsername(username);
    ValidatePassword(password);

    // create and return user
}
我的测试夹具是否应该包含针对Validate*方法中可能出现的每个验证错误的测试,还是最好将其留给一组单独的测试?或者验证逻辑应该以某种方式重构

我的理由是,如果我决定测试CreateUser中可能发生的所有验证错误,那么测试夹具将变得非常臃肿。而且大多数验证方法都是从多个地方使用的


在这种情况下,有什么好的模式或建议吗?

每个测试都应该因为一个原因失败,并且只有一个测试应该因为这个原因失败

这对编写一组可维护的单元测试有很大帮助

我将为ValidateUserDetails、ValidateUsername和ValidateUserPassword分别编写两个测试。然后您只需要测试CreateUser是否调用了这些函数


重读你的问题;看来我有点误解了

你可能会对J.p Boodhoo写的行为驱动设计风格感兴趣。

BDD正在成为一个超负荷的术语,每个人都有不同的定义和不同的工具来实现它。据我所知,JPBoodhoo正在做的是根据关注点而不是类来划分测试夹具

例如,您可以创建单独的装置来测试用户详细信息验证、用户名验证、密码验证和创建用户。BDD的思想是,通过以正确的方式命名testfixture和测试,您可以通过打印testfixture名称和测试名称来创建类似于文档的内容。按关注点而不是按类对测试进行分组的另一个优点是,对于每个夹具,您可能只需要一个设置和拆卸例程

不过,我自己对此没有多少经验

如果你对阅读更多感兴趣,JP Boodhoo在他的博客上发布了很多关于这一点的信息(见上面的链接),或者你也可以听Scott Bellware的dot net rocks插曲,他在其中谈到了类似的分组和命名测试方法

我希望这正是您想要的。

  • 让针对验证方法的单元测试(复数)确认它们的正确功能
  • 让针对CreateUser方法的单元测试(复数)确认其正确运行

如果CreateUser只需要调用验证方法,而不需要自己做出验证决定,那么针对CreateUser的测试应该确认该要求。

您肯定需要测试验证方法

不需要为所有可能的参数组合测试其他方法,只需确保执行了验证

您似乎将验证和合同设计混为一谈

验证通常用于友好地通知用户其输入不正确。它与业务逻辑密切相关(密码不够强,电子邮件格式不正确等)

契约式设计确保您的代码可以执行,而不会在以后抛出异常(即使没有异常,您也会得到异常,但要晚得多,而且可能更模糊)

关于应该包含验证逻辑的应用程序层,最好的可能是定义应用程序边界的应用程序层,它是清理应用程序输入的好地方。在这个边界内不应该有任何验证逻辑,只有契约式设计可以更早地检测错误


最后,当您想友好地通知用户他错了时,编写验证逻辑测试。否则,使用契约式设计并不断抛出异常。

我将为每个Validatexx方法添加一组测试。然后在CreateUser中创建3个测试用例,用于检查当ValidateUserDetails、ValidateUsername和ValidatePassword中的每一个失败但另一个成功时会发生什么情况。

我用于定义业务验证规则。下面是我如何测试角落案例(来自开源的示例):

其中,ValidConnection规则定义为:

public static void ValidConnection(ServiceConnection connection, IScope scope)
{
  scope.Validate(connection.Username, "UserName", StringIs.Limited(6, 256), StringIs.ValidEmail);
  scope.Validate(connection.Password, "Password", StringIs.Limited(1, 256));
  scope.Validate(connection.Endpoint, "Endpoint", Endpoint);
}

static void Endpoint(Uri obj, IScope scope)
{
  var local = obj.LocalPath.ToLowerInvariant();
  if (local == "/timeseries.asmx")
  {
    scope.Error("Please, use TimeSeries2.asmx");
  }
  else if (local != "/timeseries2.asmx")
  {
    scope.Error("Unsupported local address '{0}'", local);
  }

  if (!obj.IsLoopback)
  {
    var host = obj.Host.ToLowerInvariant();
    if ((host != "ws.lokad.com") && (host != "sandbox-ws.lokad.com"))
      scope.Error("Unknown host '{0}'", host);
  }
如果发现某个失败案例(即:添加了新的有效连接url),则规则和测试将得到更新

有关此模式的更多信息,请参阅。一切都是开源的,所以可以随意重用或提问


PS:请注意,此示例复合规则(即StringIs.ValidEmail或StringIs.Limited)中使用的基本规则是完全独立测试的,因此不需要过多的单元测试

您的业务逻辑类的职责是什么?除了验证之外,它还做些什么?我想我会根据您的上下文将验证例程移动到自己的类(UserValidator)或多个类(UserDetailsValidator+UserCredentialsValidator),然后为测试提供模拟。所以你现在的课堂看起来像:

public User CreateUser(string username, string password, UserDetails details)
{
    if (Validator.isValid(details, username, password)) {
       // what happens when not valid
    }

    // create and return user
}
然后,您可以纯粹为验证提供单独的单元测试,业务逻辑类的测试可以关注验证通过和验证失败的时间,以及所有其他测试

public User CreateUser(string username, string password, UserDetails details)
{
    if (Validator.isValid(details, username, password)) {
       // what happens when not valid
    }

    // create and return user
}