ag亚博网站

Advanced Software Development with MATLAB

Failure is the first step to trying

The official guidance on test-driven development is to follow thered-green-refactorcycle:

  1. Write a test that fails.
  2. Make it pass.
  3. Refactor.

But what’s the point in starting with a test that fails? To make sure you’ve written the right test! I encountered some unexpected behaviour recently that highlighted this point.

Imagine you have aLibrarythat aggregatesItems. AnItemcan be either aBookor aFilm, but not both at the same time. If we create aLibraryin "book mode", it should initially contain an emptyBook. If we create it in "film mode", it should initially contain an emptyFilm. Let’s start by writing a test to capture the book mode behaviour:

classdeftLibrary < matlab.unittest.TestCasemethods(Test)functionbookModeInitialisesToEmptyBook(testCase) lib = Library(Mode="book"); testCase.verifyEqual(lib.Items,Book.empty(1,0))endendend

(TheName=valuesyntax for name-value pairs wasintroduced in R2021a. It’s interchangeable with the classic(…,"Name",value)syntax.)

Let’s run the test. We expect it to fail becauseLibrarydoesn’t exist.

We’ll skip over the steps of creating a blank class definition, the subsequent test failures due to a missing constructor with input arguments and a publicItemsproperty, and iteratively adding them in.

Instead, let's jump to the implementation ofLibrarythat makes our test pass:

classdefLibrarypropertiesItems (1,:) Item = Book.emptyendmethodsfunctionlib = Library(nvp)argumentsnvp.Mode (1,1) string {mustBeMember(nvp.Mode,["book" "film"])} ="book"endendendend

We run the test and see that it passes:

So far, so good. Now we write a test to capture film mode:

functionfilmModeInitialisesToEmptyFilm(testCase) lib = Library(Mode="film"); testCase.verifyEqual(lib.Items,Film.empty(1,0))end

We run the test:

And… it passes!?

Why is it passing? We can use the debugger to inspectlib.Itemsmanually and see that it’s an emptyBookand not an emptyFilm. After some investigation, we find thatverifyEqualrelies onisequalandisequalconsiders two empties of different classes to be equal under some circumstances.

Whether or not this behaviour ofisequalis correct, the important point for us is that we’ve written the wrong test! Our implementation could have been wrong and we wouldn’t have known. We would have achieved fullcoveragebut our test would not have been contributing useful information.

We therefore need to rewrite our test to catch this issue:

functionfilmModeInitialisesToEmptyFilm(testCase) lib = Library(Mode="film"); testCase.verifyEmpty(lib.Items) testCase.verifyClass(lib.Items,?Film)end

Let’s run the updated test:

The test now fails and we can continue with our implementation, confident that our test will only pass when our implementation is correct.




Published with MATLAB® R2022a

|
  • print
  • send email

Comments

To leave a comment, please clickhereto sign in to your MathWorks Account or create a new one.