Structured Testing in VDM Kenneth Lausdahl and Peter Gorm Larsen May 2011 1 Introduction The Vienna Development Method was initially developed with proof of property in mind. However a number of test principles known for traditional software development is also supported by VDM such as unit-, integration- and system-test. Tool support exists for Unit testing and Combinatorial Testing, the latter is a part of the language where regular expressions can be used to bring a system into a specific state and from there generate tests automatically to check for violation of invariants, pre-, and postconditions in the VDM model. The exercise is divided into three assignments: 1. Specify a number of functions to be tested. 2. Create Unit tests for all functions. Include positive and negative tests. 3. Write a trace for the provided Tree model and see if the trace can detect any errors. 2 VDM Unit testing Unit testing is primary used to test small parts of a model such as a single function/- operation or class. VDM-Unit is a port of the well known JUnit for Java, it enabled a structured test through TestCases and TestSuites. The VDM Unit framework is described in more detail in chapter The VDMUnit Framework of [1], The VDM-Unit library provided for this exercise is a newer version than presented in [1], thus naming and the internal class structure is different. 1
2.1 Specify functions to be tested 1. Write a function that can reverse a sequence of integers. 2. Write a recursive power function for integers: x n e.g. 2 10 = 1024. Remember that x 0 = 1 and that x n+1 = x x n 3. Write a function which creates an onion structure. An onion is build of a small onion with a onion around thus creating a layers structure. In VDM we can see that as an empty list as the middle inner onion a list of numbers as the outer layers such that: makeonion[1, 2, 3, 4] =[1, [2, [3, [4, [], 4], 3], 2], 1] 4. Write a function which creates an onion dual structure: makedualonion[1, 2, 3, 4] =[4, [3, [2, [1, [], 1], 2], 3], 4] -- Use language version = vdm10 functions public reverse : seq of int -> seq of int reverselist == is not yet specified; public power : int * int -> int powerx,n == is not yet specified; types public T = nat1 seq of T; functions public makeonion : seq of int -> seq of T makeonionlist== is not yet specified; public makedualonion : seq of int -> seq of T makedualonionlist== is not yet specified; public static add : nat * nat -> nat adda,b == a+b; Listing 1: Signatures for the functions to test. 2.2 Create Unit tests Now develop test for each of the new functions. Produce at least one positive and one negative test of each function. So if we had an add function it could look like: class TestCaseAdd1 is subclass of TestCase protected runtest : ==> 2
runtest== asserttruefunctions add2,3=5; ; end TestCaseAdd1 class TestCaseAdd2 is subclass of TestCase protected runtest : ==> runtest== assertfalsefunctions add5,5=11; end TestCaseAdd2 Listing 2: Example of a TestCase for the add function. The TestCase s can then easily be executed trough a TestSuite as shown in listing 3 Remember to include the IO library. let ts : TestSuite = new TestSuite, result = new TestResult in ts.addtestnew TestCaseAdd1; ts.addtestnew TestCaseAdd2; ts.runresult; IO printlnresult.tostring; Listing 3: Running TestCases through a TestSuite and printing the result. 2.2.1 Simplified test structure From Overture IDE version 1.0.1 it is possible to run Unit tests in a similar way as JUnit, where the VDM interpreter is able to search for test within a TestCase or a sub class here of, and run all of tests found. This is possible by instantiating a TestSuite with a single or a set of classes which are sub classes of TestCase. class TestCaseFunctions protected testadd235 : ==> testadd235== asserttruefunctions add2,3=5; ; 3
protected testadd5511 : ==> testadd5511== assertfalsefunctions add5,5=11; end TestCaseFunctions Listing 4: A single TestCase where all prefixed test will be extracted and executed. let ts : TestSuite = new TestSuitenew TestCaseFunctions, result = new TestResult in ts.runresult; IO printlnresult.tostring; Listing 5: Running all prefixed test in TestCases through a TestSuite and printing the result. 3 Combinatorial Testing in VDM Combinatorial Testing is a good alternative to normal Unit testing for VDM models. Since formally specified VDM models often are accompanied by pre- and postconditions and also invariants the allowed behaviour of the VDM model is restricted. This can be utilised through combinatorial testing since tests can be generated and validated against these invariants, pre- and post-conditions. Combinatorial testing in VDM is build to allow a modeller to get a VDM model into a particular state and then specify a scenario by the use of constructs such as alternatives and repetition which then can be expanded to all possible combinations. This will in most cases generate a large number of tests even for small test traces. Exercise: A Tree VDM model is on purpose provided with at least one error. Specify a trace which uses the different search mechanisms of the tree. Through this identify existing errors. When a trace test fails then try to send it to the interpreter and debug the problem and fix the errors. So your tasks can be summarised as: 1. Extend the UseTree class with a more advanced trace definition. 2. Try out the Combinatorial Testing perspective CT 1. 1 This is a prototype, thus you might discover minor instabilities, but the main features should work correctly. 4
3. Try to run a full test. 4. Try to see of any of the reduced options is enough to detect the problem. Faster executions. 5. Try to send a test case to the interpreter. When doing so this should be similar to running a manually specified Unit test. 6. Fix the errors you might discover. References [1] John Fitzgerald, Peter Gorm Larsen, Paul Mukherjee, Nico Plat, and Marcel Verhoef. Validated Designs for Object oriented Systems. Springer, New York, 2005. 5