我有一个Mocking.sln
,它有两个项目:Student
(类库)和StudentStat
(控制台应用程序)
Student
项目详情如下:
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
项目详情如下:
class Program
{
static void Main(string[] args)
{
StudentInfo.CalculateTotalMarks();
}
}
此GetStudentData(
)方法从DB和输出详细信息中读取详细信息。假设这是我的生产代码,我无权更改任何内容。
我有一个GetStudentData()
的重载版本,它将filePath作为参数并从文本文件中读取详细信息。以下是方法:
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;
}
现在当我在StudentInfo.CalculateTotalMarks()
中调用StudentStat.exe
时,我希望调用此重载GetStudentData(string filepath)
而不是其他方法(从DB读取数据),并显示输出。< / p>
我听说使用NMock3
可以实现这一点。但我对此一无所知。你能否分享一下代码样本..这也有助于我学习NMock3
。任何帮助表示赞赏...
答案 0 :(得分:0)
你可以使用Test driven developement你需要做的就是重构你所拥有的代码并实现属于SOLID Principle的接口隔离,我建议你阅读{{ 3}}这是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;
}
}
注意你的方法不再是静态的,因为它们在大多数测试框架中都无法测试,我将文件路径作为类的构造函数中的依赖项传递。
所以现在你只需要选择一个依赖注入容器,你可以找到几个项目来查看roy osherov´s blog并选择你的结论。
我个人喜欢结构图和简单注入你的实现就像这样简单(this benchmark)
var container = new Container();
Configure the container (register)
container.Register<IStudentService, FileStudentService>();
然后你可以使用构造函数注入你的程序注入你的服务,这应该做的诀窍
我知道这不是一个完整的工作代码,但至少我只是想让你了解如何开始!
喝彩!