测试先行——模拟双色球开奖过程

前言

最近喜欢上了买双色球,但是每次都不知道买啥号码比较好,所以心想着不如写个可以产生双色球号码的程序,每期都生成一个号码,再买这个号码.....

再加上最近在看敏捷软件开发这本书,所以打算用测试先行的方式写这个程序。

分析

双色球,大乐透什么的都是属于慈善项目。这类慈善项目的都会有个开奖号码,并且这个开奖号码都要符合一定的规则,所以先建个慈善项目的类:

public abstract class CharitableProject
{
    public abstract int[] GenerateWinningNumbers()
    public abstract bool[] CheckRules();
}

由于我们要研究的是双色球,而双色球属于慈善项目大家族的其中一项,双色球应该继承于慈善项目。

测试先行指的是,先不写具体实现逻辑,而是先写测试用例,然后为了通过这些测试用例,补充具体实现的过程。

public class DichroicBall : CharitableProject
{
    public override int[] GenerateWinningNumbers()
    {
        return new int[0];
    }
    public override bool[] CheckRules(int[] num)
    {
        return new bool[0];
    }
}

测试:【双色球中奖号码为7个

public void TestMethod()
{
    CharitableProject charitableProject = new DichroicBall();
    var actual = charitableProject.GenerateWinningNumbers();
    var expected = new int[7];
    Assert.AreEqual(expected.Length, actual.Length);
}

运行此测试后,测试未能通过,所以得修改双色球的实现方法,使其产生7个中奖号码:

public class DichroicBall : CharitableProject
{
    public override int[] GenerateWinningNumbers()
    {
        return new int[7];
    }
}

修改之后再进行测试,测试通过了。但除了中奖号码是7个以外,号码的数值还有很多规则的。

所以希望可以自己添加规则,于是把测试用例改成以下的形式:

优化后的测试代码:

public void TestMethod()
{
    CharitableProject.CharitableProject charitableProject = new DichroicBall();
    var nums = charitableProject.GenerateWinningNumbers();

    charitableProject.AddRules(DichroicBall.ThereAreSevenNumbers);

    var actual = charitableProject.CheckRules(nums);
    var expected = new bool[1] { true };
    Assert.AreEqual(expected[0], actual[0]);
}

为了可以实现这种效果,需要使用委托,于是慈善项目类需要进行改变

public abstract class CharitableProject
{
    public List<FuncRule> Roles = new List<FuncRule>();
    public delegate void FuncRule();
    public abstract int[] GenerateWinningNumbers();
    public virtual bool[] CheckRules(int[] nums)
    {
        List<bool> roleResult = new List<bool>();
        foreach (var role in Roles)
        {
            roleResult.Add(role(nums));
        }
        return roleResult.ToArray();
    }
    public virtual void AddRules(FuncRule rule)
    {
        Roles.Add(rule);
    }
}

双色球类也要添加ThereAreSevenNumbers规则判断的方法:

public static bool ThereAreSevenNumbers(int[] nums)
{
    return nums.Length == 7;
}

测试通过,所以继续添加几个规则:如下

public void TestMethod()
{
    CharitableProject.CharitableProject charitableProject = new DichroicBall();
    var nums = charitableProject.GenerateWinningNumbers();

    charitableProject.AddRules(DichroicBall.ThereAreSevenNumbers);//一共7个数字
    charitableProject.AddRules(DichroicBall.AllNumberBetween1And33);//全部号码在1到33之间
    //前6位不重复
    charitableProject.AddRules(DichroicBall.Rule_NoRepetitionOfTheFirstSixDigits);
    //第7位在1到16之间
    charitableProject.AddRules(DichroicBall.TheFinalNumberIsBetween1And16);

    var actual = charitableProject.CheckRules(nums);
    var expected = new bool[4] { true,true,true,true };
    Assert.AreEqual(expected[0], actual[0], "7个数字");
    Assert.AreEqual(expected[1], actual[1], "1~33");
    Assert.AreEqual(expected[2], actual[2], "前6不重复");
    Assert.AreEqual(expected[3], actual[3], "7位1~16");
}

在双色球类中补充完全对应的验证方法

public static bool ThereAreSevenNumbers(int[] nums)
{
    return nums.Length == 7;
}
public static bool AllNumberBetween1And33(int[] nums)
{
    foreach (var n in nums)
    {
        if (n < 1 || n > 33)
        {
            return false;
        }
    }
    return true;
}
public static bool TheFinalNumberIsBetween1And16(int[] nums)
{
    return nums[6] >= 1 && nums[6] <= 16;
}
public static bool Rule_NoRepetitionOfTheFirstSixDigits(int[] nums)
{
    ISet<int> set = new HashSet<int>();
    for (var i = 0; i < 6; i++)
    {
        set.Add(nums[i]);
    }
    return set.Count == 6;
}

运行测试,发现后三个不通过,现在开始写生成号码的代码....

public override int[] GenerateWinningNumbers()
{
    Random random = new Random();
    var number = new List<int>(33);
    var result = new List<int>(7);

    for (int i = 1; i <= 33; i++)
    {
        number.Add(i);
    }
    //产生6个白球
    for (int i = 0; i < 6; i++)
    {
        int tempNum = random.Next(0, number.Count);
        result.Add(number[tempNum]);
        number.RemoveAt(tempNum);
    }
    result = result.OrderBy(d => d).ToList();//排序
    result.Add(random.Next(1, 16));//随机产生篮球
    return result.ToArray();
}

至此,所有的代码已经完成,开始测试下GenerateWinningNumbers方法生成的号码是否符合规则,运行测试:

测试结果

测试通过。

完成的代码如下:

UnitTestDichroicBall.cs

    [TestClass]
    public class UnitTestDichroicBall
    {
        [TestMethod]
        public void TestMethod()
        {
           CharitableProject.CharitableProject charitableProject = new DichroicBall();
           var nums = charitableProject.GenerateWinningNumbers();

           charitableProject.AddRules(DichroicBall.ThereAreSevenNumbers);//一共7个数字
           //全部号码在1到33之间
           charitableProject.AddRules(DichroicBall.AllNumberBetween1And33);
           //前6位不重复 
           charitableProject.AddRules(DichroicBall.Rule_NoRepetitionOfTheFirstSixDigits);
           //第7为在1到16之间
           charitableProject.AddRules(DichroicBall.TheFinalNumberIsBetween1And16);
           var actual = charitableProject.CheckRules(nums);
           var expected = new bool[4] { true,true,true,true };
           Assert.AreEqual(expected[0], actual[0], "7个数字");
           Assert.AreEqual(expected[1], actual[1], "1~33");
           Assert.AreEqual(expected[2], actual[2], "前6不重复");
           Assert.AreEqual(expected[3], actual[3], "7位1到16");
        }
    }

CharitableProject.cs

    public abstract class CharitableProject
    {
        public List<FuncRule> Roles = new List<FuncRule>();
        public delegate bool FuncRule(int[] nums);
        public abstract int[] GenerateWinningNumbers();
        public virtual bool[] CheckRules(int[] nums)
        {
            List<bool> roleResult = new List<bool>();
            foreach (var role in Roles)
            {
                roleResult.Add(role(nums));
            }
            return roleResult.ToArray();
        }
        public virtual void AddRules(FuncRule rule)
        {
            Roles.Add(rule);
        }
    }

DichroicBall.cs

using System;
using System.Collections.Generic;
using System.Linq;

namespace CharitableProject.Project
{
    public class DichroicBall : CharitableProject
    {
        public override int[] GenerateWinningNumbers()
        {
            Random random = new Random();
            var number = new List<int>(33);
            var result = new List<int>(7);

            for (int i = 1; i <= 33; i++)
            {
                number.Add(i);
            }
            //产生6个白球
            for (int i = 0; i < 6; i++)
            {
                int tempNum = random.Next(0, number.Count);
                result.Add(number[tempNum]);
                number.RemoveAt(tempNum);
            }
            result = result.OrderBy(d => d).ToList();//排序
            result.Add(random.Next(1, 16));//随机产生篮球
            return result.ToArray();
        }

        public static bool ThereAreSevenNumbers(int[] nums)
        {
            return nums.Length == 7;
        }

        public static bool AllNumberBetween1And33(int[] nums)
        {
            foreach (var n in nums)
            {
                if (n < 1 || n > 33)
                {
                    return false;
                }
            }
            return true;
        }
        public static bool TheFinalNumberIsBetween1And16(int[] nums)
        {
            return nums[6] >= 1 && nums[6] <= 16;
        }

        public static bool Rule_NoRepetitionOfTheFirstSixDigits(int[] nums)
        {
            ISet<int> set = new HashSet<int>();
            for (var i = 0; i < 6; i++)
            {
                set.Add(nums[i]);
            }
            return set.Count == 6;
        }
    }
}

完成之后再加个控制台程序:

    class Program
    {
        static void Main(string[] args)
        {
            CharitableProject.CharitableProject charitableProject = new DichroicBall();
            Console.WriteLine("输入需要产生的双色球号码数量,如:5");
            var count = int.Parse(Console.ReadLine());
            for (var i = 0; i < count; i++)
            {
                var nums = charitableProject.GenerateWinningNumbers();
                foreach (var num in nums)
                {
                    Console.Write(num);
                    Console.Write(" ");
                }
                Console.WriteLine();
            }
            Console.ReadLine();
        }
    }

一个可以生成双色球号码的经过测试验证的程序就完成了.....

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值