c#算法为单元测试创建伪造的加拿大社会安全号码(SIN)

本文关键字:安全 社会 号码 SIN 加拿大 算法 单元测试 创建 伪造 | 更新日期: 2023-09-27 18:25:35

我需要用c#编写单元测试,为我们的应用程序生成伪造的加拿大SIN号。在网上搜索后,以下是我的发现。我甚至不知道如何开始。特别是把每个最上面的数字和下面的数字相乘让我很困惑,因为这不是一个直接的乘法。感谢您提前提供的帮助。谢谢

这是我从谷歌搜索中得到的算法。

Algorithm _
Social Insurance Numbers are validated via a simple checksum process.
Let's use this fictitious SIN to demonstrate:
    046 454 286  <  Try substituting your SIN
    046 454 286  '  Multiply each top number
    121 212 121  /  by the number below it..
    -----------
    086 858 276  <  and get this.
             ^
             Notice here that 8*2=16, add 1 and
             6 together and get 7. If you get a
             2 digit # add the digits together.
Add all of these digits together.
0+8+6+8+5+8+2+7+6=50
                  /'
                If the SIN is valid this # will
                be evenly divisible by 10. This
                is a 'valid' SIN.

c#算法为单元测试创建伪造的加拿大社会安全号码(SIN)

这并不是一个真正的答案,但它很重要,而且评论中没有足够的空间,所以这里是


这听起来很像真实代码的目的是验证用户输入的SIN,并且您希望在测试套件中随机生成SIN来测试验证代码那错了

单元测试不应生成随机数据这并不意味着你根本不能使用数据;生成一组10000个好的SIN和10000个坏的SIN一次,保存数据,然后根据您的验证方法编写一个测试,这样您就可以获得所有这些测试的正确结果。重要的规则是:

测试是否通过不应取决于随机发生器在特定运行中生成的数据

最重要的是,失败的测试必须以可重复的方式继续失败,直到修复导致失败的代码。如果测试数据是随机的,你不能保证这一点。

稍后,您可能仍然会发现一个错误,即您的验证器在特定SIN中失败。此时的正确响应是将该SIN永久添加到测试数据中。然后修复代码。通过这种方式,你也可以避免倒退。

你可能仍然想使用它来生成你的初始测试数据,这很好,但我认为找到一个已经可以为你提供数据集的网站要容易得多。以下是谷歌的快速结果:
http://www.testingmentor.com/tools/tool_pages/ssnsin.htm

而且,为了好玩,以下是我的验证方法(未经测试,直接在回复窗口中键入):

bool IsValidSIN(string SIN)
{ 
   //normalize
   SIN = SIN.Trim();
   //account for common input formats that use - . or space as separators
   SIN = SIN.Replace(" ", "").Replace("-","").Replace(".","");
   //basic validations
   if (string.IsNullOrEmpty(SIN)) return false;
   if (SIN.Length != 9) return false;
   if (!SIN.All(c => char.IsDigit(c) ) return false;
   //checksum - we already know here that all characters are digits
   int multiplier = 0;
   int sum = 0;
   foreach (int d in SIN.Select(c => (int)c))
   {
      sum += (d * ((multiplier % 2) + 1)).ToString().Select(c => (int)c).Sum();
      multiplier ++;
   }
   return sum % 10 == 0;
}

可能有一种方法可以通过算法生成一个有效的数字,但作为快速解决方案,您可以生成一个由9个整数组成的随机序列,然后对其进行验证。重复此操作,直到获得有效的序列。以下是一个完整的实现:

class SinGenerator
{
    Random r = new Random();
    /// <summary>
    /// Generates a valid 9-digit SIN.
    /// </summary>
    public int[] GetValidSin()
    {
        int[] s = GenerateSin();
        while (!SinIsValid(s))
            s = GenerateSin();  
        return s;
    }
    /// <summary>
    /// Generates a potential SIN. Not guaranteed to be valid.
    /// </summary>
    private int[] GenerateSin()
    {
        int[] s = new int[9];
        for (int i = 0; i < 9; i++)
            s[i] = r.Next(0, 10);
        return s;
    }
    /// <summary>
    /// Validates a 9-digit SIN.
    /// </summary>
    /// <param name="sin"></param>
    private bool SinIsValid(int[] sin)
    {
        if (sin.Length != 9)
            throw new ArgumentException();
        int checkSum = 0;
        for (int i = 0; i < sin.Length; i++)
        {
            int m = (i % 2) + 1;
            int v = sin[i] * m;
            if (v > 10)
                checkSum += 1 + (v - 10);
            else
                checkSum += v;
        }
        return checkSum % 10 == 0;
    }
}

我认为他们用7代替16的原因是空间不足:如果用16代替7,其他数字将不再对齐。

我认为这更好地说明了正在发生的事情:

046 454 286  '  Multiply each top number
121 212 121  /  by the number below it..
-----------
000 000 010  <  and get this (tens are on the top)
086 858 266  <  ... ones are on the bottom.

因为最后你把所有的数字加起来,所以只有一个716对最终结果没有影响。

要生成大量有效的SIN,您可以生成随机的九位数,计算它们的"校验和",找到该校验和除以10的余数,并调整位置1、3、5、7或9中的一个或多个数字以获得正确的校验和。

您可以创建一个9个整数/数字的列表,生成前8个数字(随机数在0到9之间),然后将最后一个数字设置为一个值,以便相加的9个数字可以除以10。

这是在随机生成所有其他8位后计算第9位的方法

d9=10-(d1+d2+d3+d4+d5+d6+d7+d8)%10

var nas = "046 454 286".Where(x => !Char.IsWhiteSpace(x)).ToList();
var check = "121 212 121".Where(x => !Char.IsWhiteSpace(x)).ToList();
var result = "";
for(int i = 0; i < nas.Count(); ++i){
    int tmp = (int)(nas[i]) * (int)(check[i]);
    var val = tmp < 10 ? tmp : tmp - 9;
    result += val;
}
var sum = result.Aggregate(0, (acc, item) => acc + (int)(item));
if(sum % 10 == 0) Console.WriteLine("valid!"); else Console.WriteLine("invalid!");