CSC/ECE 517 Fall 2010/ch6 6b HA: Difference between revisions
(→C#) |
(→C#) |
||
Line 155: | Line 155: | ||
return ( a / b ); } | return ( a / b ); } | ||
When the code is build in release mode, the Debug.Assert method disappear. If a developer wants to check his assertions then instead of writing Debug.Assert, he can use Trace.Assert which stays there in the Release version too. Please note that, as explained above, this may inflate the size of the binary executable and may also lead to performance issues. | |||
// Assertions remain in the release version too | |||
//Function to divide an integer | |||
int IntDivide ( int a , int b ) | |||
{ Trace.Assert ( b != 0 ); | |||
return ( a / b ); } | |||
==Java== | ==Java== |
Revision as of 18:35, 14 November 2010
Assertions in Object Oriented Language
In most of the programming languages assertions are statements that enables developers to test assumptions about the code they write. For example, if a developer writes a function that checks if two strings are equal or not, he might assert that the lengths of the strings are equal. Assertions are always made using a Boolean expression which a developer assumes will be true when the assertion gets executed. If the assertion is false then error will be thrown at the runtime.
Introduction
By doing this verification on that Boolean expression, assertion confirms developer’s assumptions about the program behavior and hence increase the confidence that an application is as per the requirement and free of errors. Research has shown that by writing assertions while a developer is programming is one of the efficient ways to detect and fix bugs. Also, assertions acts as a documentation to the inner working of the code and hence improves the maintainability of the code which is an important aspect of software engineering.
While debugging code, developers can write assertions in two forms, simplest being –
assert expression1 ;
where the expression1 is a Boolean expression which if true, verifies the correctness of your code and if false, throws an error during runtime showing you the assertion without giving you any details about the error.
Second form of assertion is
assert expression1 : expression2
where expression1 is again a Boolean expression and expression2 is an expression that has some value. The value can be the error message or may be related to the some value related to the current code execution. By doing this a developer can know much more details about the error which is causing that assertion failure and allow them to diagnose and fix it. This form of assertion is usually used when a developer is not testing the app as it gives some additional information to the 3rd party developers/testers that might help them diagnose and fix the problem.
As stated above assertions help in verifying the correctness of a program. However, assertions should be carefully used as in some cases it can be expensive to evaluate the Boolean expression of an assertion. For example, if a developer is writing a program for binary search, then he may need to verify that the list should be sorted. Now if he uses some method in assertions for doing that, it can affect the performance of the app. For this reasons, almost every compiler supports enabling and disabling of assertions so that the developers don’t need to remove their code for assertions everywhere and to increase the performance and quality of the software.
Usage
Types of assertions
There are primarily three types of assertions in programming languages namely -
1) Preconditions: In such type of assertion, certain conditions should have been met before calling a method. These conditions are usually checked at the entry point of the calling function.
2) Postconditions: It defines the functionality of the method. By asserting post condtions at the end of the method developers check the output with the desired requirements
3) Invariants: These assertions define conditions over a specified region of the code and verifies state-space consistency for a class. It can be evaluated at the entry or exit points of the method.
Design by contract
Design by contract or Programming by Contract is a design approach for client and suppliers developing a computer software. According to this approach, software designers (or clients) define verifiable interface specifications for software modules with preconditions, post conditions and invariants. These specifications for software modules are known as contracts. A software designer should specify the pre and post conditions i.e before the execution what conditions should be true and after the execution what things should be true. In object oriented architecture, preconditions can be weakened by subclasses and post conditions can be strengthened.
As an example, if a function has some specified pre condition then client would held responsible if the condition fails inside the function. It also means that the function should not have to test for that precondition and similarly client should not have to write try/catch while calling that function. Client or a software designer should ensure that they will meet the pre-condition by doing runtime tests or may be by assuming some condition which will be satisfied based on the function requirements and specifications.
This explicit definition of pre and post conditions forces software designers to think about the requirements of the specification and their implementation. Also for developers, these assertions may also influence declaration and throwing of runtime exceptions and try/catch block. The design by contract approach eliminates unnecessary redundant checks. Pre and post conditions reduce the need for checking the need for verifying the same condition both at client as well as developer’s side.
For example if a method has specified some pre-condition then the failure of that condition is the responsibility of the client of the method. The method should not have to declare a checked exception for that condition, it should not have to test and throw for that condition, and likewise the client should not have to try/catch for that condition.
Assertions in Object Oriented Language (show pictures showing assertion error.)
C++
Unlike other languages, assertions in C++ are implemented using assert macro. The argument provided to the assert statement should be true when macro is executed or else the program aborts and displays an error message. By default the assert macro in C++ comes in the standard library and is usually defined in assert.h. However a developer can define their own macros to have a better functionality.
#ifndef DEBUG #define ASSERT(x) #else #define ASSERT(x) \ if (! (x)) \ { \ cout << "ERROR!! Assert " << #x << " failed\n"; \ cout << " on line " << __LINE__ << "\n"; \ cout << " in file " << __FILE__ << "\n"; \ } #endif
Sample program which uses an assert macro -
/*using the assert macro defined in assert.h */ #include <stdio.h> #include <assert.h> main() { int num; printf("\nInput an integer value"); scanf("%d", &num); assert(num >= 0); printf("Value entered is %d.\n", num); return 1; }
The purpose of proposed ANSI C++ assertion is to provide a default assertion behavior similar to C. But since C++ is object oriented, these assertions rely on extra C++ features like templates and exceptions to provide more generic and powerful support than just simple macros of C.
Ruby
By default Ruby doesn't have native support for assertions, however we can include Test::Unit framework if we want to work with assertions. Assertions are part of Test::Unit::Assertion module. Test::Unit::Assertions contains the standard Test::Unit assertions. Assertions is included in Test::Unit::TestCase.
In Ruby, the message given to each assertion is shown at the failure. Also a developer can customize his assertions based on assert_block.
Assertions at the run-time
Ruby has support for well defined assert functions. These are defined as public instance methods in the Assertions module. Few of the examples are given below.
require "test/unit" # Assertion of boolean condition a=32 assert a>16 # true #Assertion on Equality result=(palindrome(str))?"yes":"no" assert_equal(result,"yes", "String is a palindrome") #Assertion to check if the object is nil assert_nil [1, 2].uniq! #Assertion to match regex. assert_match(/\d+/, 'five, 6, seven')
Python
Unlike Ruby, assertions in python are built into the language. However, syntax for the assertion is very similar to Ruby. As per the Python documentation generic syntax for writing assertions is
assert_stmt ::= "assert" expression ["," expression]
If the assertion fails, then an exception Assertion error is thrown that results in termination of the running program.
Assertions at the Runtime
#!/usr/bin/python def Max(a,b) assert(a!=b), "Both the values are equal" if a>b return a if b>a return b print Max(5,6) print Max(4,4)
This would print the following result -
6 Traceback (most recent call last): File "test.py", line 13, in <module> print Max(4,4) File "test.py", line 5, in Max assert(a!=b), "Both the values are equal" AssertionError: Both the values are equal
C#
C# is modern, general purpose object oriented language. In C# one can use assertions either using the Debug Class or the Trace Class which are defined in System.Diagnostics namespace. Since Debug class methods are not included in a release version of an application, the assertions do not inflate the code for or increase the size of the binary.
Assertions during Runtime
//Function to divide an integer int IntDivide ( int a , int b ) { Debug.Assert ( b != 0 ); return ( a / b ); }
When the code is build in release mode, the Debug.Assert method disappear. If a developer wants to check his assertions then instead of writing Debug.Assert, he can use Trace.Assert which stays there in the Release version too. Please note that, as explained above, this may inflate the size of the binary executable and may also lead to performance issues.
// Assertions remain in the release version too //Function to divide an integer int IntDivide ( int a , int b ) { Trace.Assert ( b != 0 ); return ( a / b ); }