How would you apply unit testing in this situation?

I have never written unit tests before, for various reasons. I have a chance to write tests now, conveniently, because I have a small application to do from scratch.

However, I am a little puzzled. It is assumed that the application uses a printer with a smart card reader to program data on a smart card. Thus, the sequence of actions: create the device context, set the printer mode, initialize the document, insert the card into the printer, connect to the card using a reader, write something to the card, display the card, complete the document, and utilize the device context.

Well, unit tests should test one function for each test, and each test should run independently of the result of other tests. But let's see - I can not check the recording on the smart card if I did not correctly place it in the printer, and if I did not connect to it. And I can not scoff at this software - I can only check if the spelling really happened if the real card is correctly installed and connected. And if the connection to the card fails, there is no way to check the recording on the card - so the principle of test independence is violated.

So far I have come up with such a test (there are other tests that are "correct" and check other things)

[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"); } 

The test works, but the principles are violated - it tests several functions and many of them. And each next function depends on the result of the previous one. Now we turn to the question: how do I approach unit testing in these circumstances?

+4
source share
3 answers

Your test code is what is often called an integration test. In short, integration tests are often defined as tests that verify integration between system components. Although, as David Reis notes, unit tests often test individual methods.

Both classes of tests are useful. Integration tests, like yours, implement the system from start to finish, making sure everything works well. But they are slow and often have external dependencies (for example, a card reader). Unit tests are smaller, faster, and more focused, but it’s hard to see a forest for trees if all you have is unit tests.

Put the device tests in a separate directory from your integration tests. Use continuous integration. Run your integration tests, perhaps only a few times a day, because they are slower and require more configuration / deployment. Run your unit tests periodically.

Now, how do you unit test your specific situation when the methods depend on other methods? It is unclear how much code you control and how much in libraries, but in your code, learn how to make the most of dependency injection (DI).

Suppose your reading method looks something like this (in pseudocode)

 boolean WriteToCard(String data){ // do something to data here return ICE_API.WriteToCard(ICE_API.SOME_FLAG, data) } 

Well, you should change this to something like:

  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) } 

Then in your test for WriteToCard in setup you would do

 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. 
+4
source

This is not like unit test. Unit test should be fast and pushy, i.e. You don’t need to check if the operation was actually performed in hardware. I would classify this code as β€œtest automation," since you need to complete this task and make sure something happens.

The code is also procedural and looks very complicated. Using multiple statements in the same test method indicates that it should be separated.

My preferred unit testing link is the Misko Hevery website . Hope this helps!

+4
source

In a series of tests that depend on eachother, there is nothing natural except that you will not get a complete list of crashes if several things are broken, because the first test will fail with an error.

One way to fix this is to create a test initialization procedure (using the [SetUp] attribute in the [SetUp] class), which brings the system to a known state before executing a single test.

Please also note that this scenario is not entirely suitable for unit testing because it requires potential manual actions outside of the software. Unit tests are inherently better suited for testing software modules that do not interact with anything unreproducible. You can perform operations on the abstract reader API (by creating an interface for the necessary operations and the class that passes these calls to the actual API), and then you can use the mock object to pretend so you can test the basic logic of your classes without relying on hardware.

Then you can implement testing the real real API, either in the unit test, or in something else that requires minimal interaction with the person ... basically you have to encapsulate the person in your test process; )

+3
source

All Articles