c# - 在这种情况下,您将如何应用单元测试?

标签 c# unit-testing nunit

由于各种原因,我以前从未编写过单元测试。我现在有机会舒适地编写测试,因为我有一个小应用程序要从头开始制作。

不过,我有点不解。该应用程序应该使用带有智能卡读卡器的打印机来对智能卡上的数据进行编程。下面是操作顺序:创 build 备上下文、设置打印机模式、初始化文档、将卡片送入打印机、使用读卡器连接卡片、向卡片写入内容、移出卡片、结束文档、处理设备上下文。

好的,单元测试应该为每个测试测试一个功能,并且每个测试应该独立于其他测试的结果运行。但是让我们看看 - 如果我没有在打印机中正确放置它并且没有连接到它,我无法测试写入智能卡。而且我不能通过软件来模拟这个——我只能测试如果真正的卡被正确定位并连接到,写入是否真的发生了。如果连接卡失败,则无法测试写入卡 - 因此测试独立性原则被打破。

到目前为止,我想出了一个这样的测试(还有其他“适当”的测试,也测试其他东西)

[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 提到的,单元测试通常会测试单个方法。

这两类测试都很有用。集成测试,就像你的一样,从头到尾测试系统,确保一切都能很好地协同工作。但是它们很慢并且通常有外部依赖项(如读卡器)。单元测试更小、更快且高度集中,但如果您只有单元测试,就很难只见树木不见森林。

将单元测试与集成测试放在不同的目录中。使用持续集成。一天可能只运行几次集成测试,因为它们速度较慢并且需要更多的设置/部署。始终运行单元测试。

现在,您如何对方法依赖于其他方法的特定情况进行单元测试? 目前还不清楚您控制了多少代码与库中有多少代码,但在您的代码中,请学会尽可能多地使用依赖注入(inject) (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.

关于c# - 在这种情况下,您将如何应用单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1175549/

相关文章:

c# - ASP.NET Core 2.0 webApp 在发布到 iis 后显示 'view Index was not found...'

c# - 为什么我在 MSpec 中得到 get_ConsoleOut() 的 MissingMethodException?

c# - 使用 nunit 测试事件

c# - 建模 NHibernate 查询

c# - 跟踪方法执行时间

c# - 类库中的反射 GetMethod 问题(可移植)

c# - 在 Moq 中设置时返回值无效时如何运行函数

java - 对传递给静态方法的值进行单元测试

c# - 使用基于约束的断言提高断言 IsNotNullOrEmpty 的可读性

nunit - 最小起订量 4.0 : The type initializer for 'Moq.Mock` 1' threw an exception