如何在这种情况下应用单元测试?
由于各种原因,我从未写过单元测试。我现在有机会轻松地编写测试,因为我有一个小程序可以从头开始制作。如何在这种情况下应用单元测试?
但是,我有点困惑。应用程序应该使用带有智能卡读卡器的打印机在智能卡上编写数据。下面是一系列操作:创建设备上下文,设置打印机模式,初始化文档,将卡片送入打印机,用读卡器连接卡片,向卡片写入内容,移出卡片,结束文档,处理设备上下文。
好吧,单元测试应该为每个测试测试一个函数,并且每个测试都应该独立于其他测试的结果运行。但是让我们看看 - 如果我没有在打印机中正确定位并且没有连接到智能卡,我就无法测试写入智能卡。而且我不能通过软件来嘲笑 - 我只能测试如果真正的卡被正确定位和连接,写入是否真的发生。如果连接到卡将失败,没有办法测试写入卡 - 所以测试独立性原则被打破。
到目前为止,我想出了这样一个测试(也有其他的测试,是“适当的”,太考其他的东西)
[Test]
public void _WriteToSmartCard()
{
//start print job
printer = new DataCardPrinter();
reader = new SCMSmartCardReader();
di = DataCardPrinter.InitializeDI();
printer.CreateHDC();
Assert.AreNotEqual(printer.Hdc, 0, "Creating HDC Failed");
Assert.Greater(di.cbSize, 0);
int res = ICE_API.SetInteractiveMode(printer.Hdc, true);
Assert.Greater(res, 0, "Interactive Mode Failed");
res = ICE_API.StartDoc(printer.Hdc, ref di);
Assert.Greater(res, 0, "Start Document Failed");
res = ICE_API.StartPage(printer.Hdc);
Assert.Greater(res, 0, "Start Page Failed");
res = ICE_API.RotateCardSide(printer.Hdc, 1);
Assert.Greater(res, 0, "RotateCardSide Failed");
res = ICE_API.FeedCard(printer.Hdc, ICE_API.ICE_SMARTCARD_FRONT + ICE_API.ICE_GRAPHICS_FRONT);
Assert.Greater(res, 0, "FeedCard Failed");
bool bRes = reader.EstablishContext();
Assert.True(bRes, "EstablishContext Failed");
bRes = reader.ConnectToCard();
Assert.True(bRes, "Connect Failed");
bRes = reader.WriteToCard("123456");
Assert.True(bRes, "Write To Card Failed");
string read = reader.ReadFromCard();
Assert.AreEqual("123456", read, "Read From Card Failed");
bRes = reader.DisconnectFromCard();
Assert.True(bRes, "Disconnect Failde");
res = ICE_API.SmartCardContinue(printer.Hdc, ICE_API.ICE_SMART_CARD_GOOD);
Assert.Greater(res, 0, "SmartCardContinue Failed");
res = ICE_API.EndPage(printer.Hdc);
Assert.Greater(res, 0, "End Page Failed");
res = ICE_API.EndDoc(printer.Hdc);
Assert.Greater(res, 0, "End Document Failed");
}
的测试工作,但原则被打破 - 它测试多种功能,以及其中的很多功能。以下每个函数都取决于前一个的结果。现在,我们来回答这个问题:在这种情况下我应该如何进行单元测试?
您的测试代码就是通常所说的集成测试。简而言之,集成测试通常被定义为检查系统组件之间集成的测试。正如David Reis所说,单元测试通常会测试单个方法。
这两类测试都很有用。与您一样,集成测试从头至尾地运行系统,确保一切都很好地协同工作。但是它们很慢并且经常具有外部依赖性(如读卡器)。单元测试更小,更快,高度集中,但如果你所有的都是单元测试,很难看到树木。
将您的单元测试放在与集成测试不同的目录中。使用持续集成。可能每天只运行几次集成测试,因为它们速度较慢并且需要更多的安装/部署。始终运行你的单元测试。
现在,你如何测试你的方法依赖于其他方法的特定情况? 目前还不清楚您控制的代码量与库中的代码量有多大,但在您的代码中,尽可能多地使用依赖注入(DI)。
假设你的读者的方法看起来是这样的(伪代码)
boolean WriteToCard(String data){
// do something to data here
return ICE_API.WriteToCard(ICE_API.SOME_FLAG, data)
}
那么你应该能够改变这是这样的:在您的测试中
ICE_API api = null
ICE_API setApi(ICE_API api) {
this.api = api
}
ICE_API getApi() {
if (api == null) {
api = new ICE_API()
}
}
boolean WriteToCard(String data){
// do something to data here
return getApi().WriteToCard(ICE_API.SOME_FLAG, data)
}
那么对于WriteToCard你会做的设置
void setup()
_mockAPI = new Mock(ICE_API)
reader.setApi(_mockAPI)
void testWriteToCard()
reader.writeToCard("12345")
// assert _mockAPI.writeToCard was called with expected data and flags.
本质上没有什么依赖于彼此的一系列测试的错误,除非多个事情都被破坏,否则您将无法获得完整的失败列表,因为第一次失败的测试将是一个报道。
您可以通过创建测试初始化例程(在您的[TestFixture]
类中使用[SetUp]
属性)来使该系统进入已知状态,然后再执行单个测试。
请注意,虽然这种情况并不完全适用于单元测试,因为它需要潜在的软件手动步骤。单元测试本质上更适合测试不与任何不可重复的任何事物交互的软件模块。您可能希望在读取器API摘要上进行操作(通过为您需要的操作创建接口,以及将这些调用传递给实际API的类),然后您可以使用模拟对象来伪装成阅读器,因此您可以测试课程的主要逻辑,而无需依赖硬件。
然后,你可以实现实际真实API的测试,既可以进行单元测试,也可以执行其他需要一些最小人机交互的事情......基本上,你会将人体封装在测试过程中; )
这看起来不像单元测试。单元测试应该是快速和有说服力的,也就是说,你不需要检查硬件中是否发生了操作。我会将此代码分类为“测试自动化”,因为您需要执行此任务并确保发生了某些事情。
该代码也是程序性的,看起来很难测试。在相同的测试方法中使用几个断言表明它应该被分割。
我个人的单元测试参考文献是Misko Hevery's site。希望能帮助到你!
那么这就是我的问题所在 - 它不是真正的单元测试!我有一个看起来更像是单元测试)等检查,即 [测试] 公共无效ConverLongStringToHexArray( { 的byte []预计=新的字节[] {0X31,0x32,0x33,0x34,0x35,0X31,0x32, 0x33,0x34,0x35,0x31,0x32,0x33,0x34,0x35,0x36}; byte [] result = reader.StringToHexArray(“1234512345123456zzzzzzzzzzzzzzzzzzzzzzzzz”); Assert.AreEqual(预计,结果,“将长字符串转换为字节数组时出错”); } 但是大的一个不容易划分 感谢您的链接btw,非常有用 – Evgeny 2009-07-30 05:26:44
在评论中放置代码是一个坏主意...... – Evgeny 2009-07-30 05:27:23
任何获得访问C#包装的机会?我们正在做同样的事,但诉诸VB6和C++代码。希望能够在更好的IDE和框架中完成所有任务。 – fuzz 2012-08-22 06:23:50