0

I have a Mocking.sln which has two projects: Student (Class Library) and StudentStat (Console Application)

Student project has below details:

    private static Dictionary<int, int> GetStudentData()
        {
            // Key: Student ID Value: Total marks(marks in math + marks in physics)
            Dictionary<int, int> studentResultDict = new Dictionary<int, int>();

            using (SqlConnection con = new SqlConnection("Data Source=.;Initial Catalog=myDB;Integrated Security=true"))
            {
                con.Open();
                string cmdStr = "Select * from Student";
                using (SqlCommand cmd = new SqlCommand(cmdStr, con))
                {
                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            studentResultDict.Add((int)reader["ID"], (int)reader["MarksInMath"] + (int)reader["MarksInPhysics"]);
                        }
                    }
                }
            }

            return studentResultDict;
        }

     public static void CalculateTotalMarks()
        {
            Dictionary<int, int> studentResultDict = GetStudentData();
            foreach (var item in studentResultDict)
            {
                Console.WriteLine("{0}\t{1}", item.Key, item.Value);
            }
        }

StudentStat project has below details:

class Program
    {
        static void Main(string[] args)
        {
            StudentInfo.CalculateTotalMarks();
        }
    }

This GetStudentData() method read details from DB and output details. Suppose this is my production code and I have no permission to change anything.

I have a overloaded version of GetStudentData() which takes filePath as a parameter and read the details from text file. Below is the method:

private static Dictionary<int, int> GetStudentData(string filepath)
    {
        Dictionary<int, int> studentResultDict = new Dictionary<int, int>();
        foreach (var item in File.ReadAllLines(filepath))
        {
            string[] data = item.Split(new string[] { "|" }, StringSplitOptions.RemoveEmptyEntries);
            studentResultDict.Add(Convert.ToInt32(data[0]), Convert.ToInt32(data[1]) + Convert.ToInt32(data[2]));
        }

        return studentResultDict;
    }

Now when I call StudentInfo.CalculateTotalMarks() in StudentStat.exe, I want this overloaded GetStudentData(string filepath) will be called instead of the other method (which reads data from DB), and shows me output.

I heard that this will be possible using NMock3. but I dont have any idea on that. Can you please share soem code samples..It will help me to learn NMock3 also. Any help is appreciated...

4

1 回答 1

0

您可以使用测试驱动的开发,您需要做的是重构那里的代码并实现作为SOLID 原则一部分的接口隔离,我建议您阅读roy osherov 的博客,它是TDD,我只是通过阅读单元测试艺术的书来学习 TDD(它现在可能已经过时了),但简而言之,你需要做的就是解耦你的代码并分离你的实现,并为它定义一个合同.

例如,您可以拥有这个接口,它将定义您需要做的事情的方法。

interface IStudentService{
   Dictionary<int, int> GetStudentData() 
}

然后你可以有两种不同的实现

为简单起见,我将复制并粘贴您的 SQL 代码,但您必须记住,在您的 TDD 方法中,SQL 命令也需要被视为依赖项。

public class DBStudentService:IStudentService{

    public Dictionary<int, int> GetStudentData()
    {
    // Key: Student ID Value: Total marks(marks in math + marks in physics)
        Dictionary<int, int> studentResultDict = new Dictionary<int, int>();
        using (SqlConnection con = new SqlConnection("Data Source=.;Initial Catalog=myDB;Integrated Security=true"))
            {
                con.Open();
                string cmdStr = "Select * from Student";
                using (SqlCommand cmd = new SqlCommand(cmdStr, con))
                {
                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            studentResultDict.Add((int)reader["ID"], (int)reader["MarksInMath"] + (int)reader["MarksInPhysics"]);
                        }
                    }
                }
            }

            return studentResultDict;
        }
}

然后创建一个不同的实现让我们说

   public class FileStudentService:IStudentService{
        steing _filepath;
        Public FileStudentService(string filepath){
          _filepath = filepath;
        }

        public Dictionary<int, int> GetStudentData()
        {
            Dictionary<int, int> studentResultDict = new Dictionary<int, int>();
            foreach (var item in File.ReadAllLines(_filepath))
            {
                string[] data = item.Split(new string[] { "|" }, StringSplitOptions.RemoveEmptyEntries);
                studentResultDict.Add(Convert.ToInt32(data[0]), Convert.ToInt32(data[1]) + Convert.ToInt32(data[2]));
            }

        return studentResultDict;
        }

    }

请注意,您的方法不再是静态的,因为它们无法使用大多数测试框架进行测试,并且我将文件路径作为类的构造函数中的依赖项传递。

所以现在你只需要选择一个依赖注入容器,你可以找到几个项目,看看这个基准并选择你的结论。

我个人喜欢结构图和简单的注入器,注册你的实现就像这样简单(使用 SI

var container = new Container();
Configure the container (register)
container.Register<IStudentService, FileStudentService>();

然后您可以使用构造函数注入将您的服务注入您的程序,这应该可以解决问题

我知道这不是一个完整的工作代码,但至少我只想告诉你如何开始!

干杯!

于 2013-08-08T05:21:35.303 回答