Uploaded by Ne Nre

ASE2

advertisement
NOTTINGHAM TRENT UNIVERSITY
SCHOOL OF SCIENCE AND TECHNOLOGY
Advanced Software Engineering Assignment 2
by
Aras Butrimanskas
in
2022
Table of Contents
Task 1: Explaining Test-driven Development ............................................................ 2
Introduction ....................................................................................................... 2
Test-driven Development Cycles........................................................................... 2
Benefits ............................................................................................................ 3
Drawbacks......................................................................................................... 4
Conclusion ......................................................................................................... 4
Task 2: Applying Test-driven Development ............................................................... 5
Introduction ....................................................................................................... 5
Creating Automated Unit Tests ............................................................................. 6
Developing the Algorithms ................................................................................... 7
Bonus – Property-Based Tests .............................................................................. 8
Task 3: Reflecting on Experiences ........................................................................... 9
References ......................................................................................................... 10
1
Figure 1 - Unit Test Example .................................................................................. 6
Figure 2 - Test Formatting ...................................................................................... 6
Figure 3 - <=.005 Output using *1e2/1e2 ................................................................ 7
Figure 4 - decimalPoint Function ............................................................................. 7
Figure 5 - incomeTax function ................................................................................. 8
Figure 6 - Output of the Tests ................................................................................. 9
Task 1: Explaining Test-driven Development
Introduction
Test-driven Development (TDD) is a practice of software development in which unit tests
are created first to cater to the functionality of the software prior to the code. That
means the test cases are being deployed and only then the code is written according to
the test cases to pass the test. This practice of software development has been used
since around 1960s (Bhat and Nagappan, 2006), with the first recorded form of Testdriven Development being Mercury Space Program. However, Kent Beck is considered
the pioneer of this practice and is additionally known as the creator of Agile and Extreme
Programming methodology, who has written the first-ever test framework, which dates
back to 1994, called SUnit (Agarwal and Deep, 2014)(Beck, 2003). Since both
methodologies are currently widely accepted and work flawlessly together with Testdriven Development, it became very popular in various developer communities as well as
corporations and remains in usage until now.
Test-driven Development Cycles
According to Kent Beck, the cycles of Test-driven Development are writing a test,
making it run, and making it right (Beck, 2003). Even though the book is relatively
outdated and is written to teach unit testing on SmallTalk programming language, the
main principles of these cycles remain relevant to this day.
2
Writing a test is relatively obvious since Kent Beck does not use any wordplay in this
statement. It is simply an act of writing a test that will pass when the specification of the
functionality is met.
Making it run is referred to as writing a very primitive code that passes the created test.
This stage does not require to have a fully functional code of high quality, as it will be
addressed during the last stage.
Finally, making it right is referred to as refactoring the code. This is when a code of high
quality should be implemented instead of the primitive one. According to the
requirements of the software, the code might also be refactored further by removing
duplicate code, rearranging the location of the code, splitting, or combining the methods,
etc. It is a good idea to also re-run the tests to make sure that the tests still pass after
refactoring the code.
The cycle is then repeated for each additional functionality until all the requirements are
met and all of the functionalities, according to the requirements, are implemented.
Benefits
There are a few major advantages to Test-driven Development method as opposed to
the traditional method of developing first.
By utilizing Test-driven Development, developers generate a code of much higher quality
and cleaner design by creating the tests incrementally (Shull et al., 2010). This results in
a source code that is easily maintainable and in turn tasks transferrable without issues
among the members of the development team.
Additionally, the tests that are created document the code indirectly, since they portray
how the code should function or how various interfaces must be used. This helps get rid
of the need to allocate the time for code documentation, which is very time-consuming
when it comes to software development.
3
And lastly, it could be argued that Test-driven Development promotes higher
productivity. This method helps developers stay confident with their code since they will
naturally produce fewer bugs which will save time as a result. The study conducted by
the students of the University of Kansas on the productivity of the Test-driven
Development states that the team that practiced Test-driven Development has produced
twice as many features as the team that practiced Test-last development (Janzen and
Saiedian, 2006).
Drawbacks
On the other hand, there are a bunch of drawbacks to using Test-driven Development as
opposed to Test-last development.
Even though the code becomes easily maintainable after the code is complete, the tests
have to be catered to the requirements if they change during the development. Due to
this reason, the time it takes to implement the new requirements increases, as
requirement refactoring ruins the existing tests, giving the developers almost twice the
work as opposed to Test-last development.
Surprisingly, another research conducted at the University of Jyvaskyla suggests that
productivity actually decreases more so than increases contrary to what has been stated
in the report from the University of Kansas (Kollanus, 2010). The studies concluded that
Test-driven Development requires increased effort, which naturally increases the time
taken to complete the development process.
Conclusion
Test-driven Development is a useful practice and a good alternative method to develop
software whether it would be for a school project, a personal project, or the industry.
However, looking back at the drawbacks as well as the benefits, it seems clear that Testdriven Development could be similar to a double-edged sword. There are reports that
argue with and against this approach to increase the productivity of the development
4
process. Even though the productivity aspect is questionable, it is evident that
production quality increases significantly when utilizing Test-driven development,
however, with the price of additional time that must be dedicated for unit tests which
could be expensive.
The results of the Test-driven Development research conducted by Kollanus state that 16
out of 22 studies suggest that Test-driven Development increases external code quality.
Most of the studies report that Test-driven Development does not affect internal code
quality and as stated previously, the majority of the studies found that Test-driven
Development decreases productivity (Kollanus, 2010). That means there is no correct
answer whether Test-driven Development is superior to other practices or not. It all
depends on the expertise of developers in creating tests, the project, its size, and its
difficulty, making it relatively situational.
It could be argued that for smaller projects or easy tasks, using Test-driven
Development approach is useless since it only increases the time taken to develop the
functioning code. However, it is a very good idea to make use of Test-driven
Development for projects that are bigger in size and where the possibility of various
unnoticed errors or bugs increases. This approach will make the software more
bulletproof and might even increase the productivity of the developers that work on
those projects.
Task 2: Applying Test-driven Development
Introduction
For task 2 of the assignment the decision to implement a Net Income Calculator using
Haskell has been made using Test-driven Development, as specified in the brief of the
assignment. HUnit library has been utilized to create automated unit tests and
QuickCheck has been utilized to develop property-based tests.
5
To develop the Net Income Calculator, the Test-driven Development cycles that were
mentioned in the previous chapter will be closely followed during the development
process.
Creating Automated Unit Tests
For every function, three tests with different values were created to test whether the
output of the function is correct. This has been achieved using assertEqual, which is an
assertion that expects a value depending on the value that is being passed in. The figure
below shows an example of how it was implemented in this solution.
Figure 1 - Unit Test Example
Since there are multiple tests, the tests have been named to the function that is being
tested accordingly. Additionally, a function was created that formats the tests into a
more readable format. This solution has been introduced by Dr Neil Sculthrope in
Advanced Software Engineering lecture 16. The figure below shows how it was achieved.
Figure 2 - Test Formatting
Every test was added into a list to make it easier to run from within the do block.
And finally, the tests have been run to ensure that each one of them works. Since there
were no functions implemented at the time, all of the tests have failed, indicating that
the tests in fact work.
6
Developing the Algorithms
After setting up the tests with values and correct results, the algorithms were developed.
Instead of using Ints as the data type the decision was made to use Double data type to
be able to output pennies as well. That means there was a requirement to somehow
round up the two last decimal points and output those instead of a full double value. To
achieve this, simply adding *1e2/1e2 after every output seemed to solve the issue,
however, resulted in a slight problem. If the ending of the decimals were equal or
smaller than 5, it would not round the number up correctly for an unknown reason. The
figure below portrays this problem.
Figure 3 - <=.005 Output using *1e2/1e2
To combat this, instead of using *1e2/1e2, a function was created that utilized floor to
return the two decimal points that were required along with the value itself. However, it
does not round up the decimal points itself, meaning that the Net Income Calculator
might have an error of calculation of 1 penny, which was deemed insignificant to the
developer of the Net Income Calculator.
Figure 4 - decimalPoint Function
According to the test cases, the functions were created that would comply with the
values that are being expected from them by the tests. Two more functions were
7
implemented that would calculate the income after taxes with and without student
income, by running the income value through every function and deducting the results
from the given income value.
After every new created function, the tests have been run to ensure that the code does
not break, and all prior tests pass successfully. There was no need to refactor the code
or improve the code quality, as Kent Beck in his book mentions being the last cycle
(Beck, 2003), for the Net Income Calculator, since it is a relatively small project and
every function requires to have specific math to calculate the tax thresholds and
different tax rates, meaning there wasn’t much space left for improving the code quality
anyway. A sample of one of the functions is portrayed in the figure below.
Figure 5 - incomeTax function
Bonus – Property-Based Tests
The property-based tests do not seem to be useful for this project, however, the tests
were implemented anyway to portray knowledge. The property-based tests select a
hundred random Double values, however, as stated previously, they are not that useful
because the values have negative values, or values with a lot of decimal points in them
and do not replicate the income values very well.
To do this, the functions were defined into properties and then tested by invoking
quickCheck. Additionally, a single verboseCheck has been invoked to portray why
property-based tests do not seem to be useful and what values are being passed into the
functions for testing.
8
Figure 6 - Output of the Tests
Task 3: Reflecting on Experiences
According to the implementation process of the Net Income Calculator, the experience of
using Test-driven Development was good overall. Creating tests first has definitely made
implementation of each deduction function easier. Arguably if the task was to utilize the
standard Test-last approach, it would have cost the developer more time to develop the
Net Income Calculator for one simple reason. The functions mainly consist of pure math,
therefore the chance of slight hiccups in the code increases, meaning that the developer
would have to create tests after finishing the development process and then go through
the code again, doubling the amount taken for the implementation process. Test-driven
Development helped the developer get rid of this issue by having the tests in place first,
with the only requirement being to create functions that would cater to the tests,
simplifying the whole process.
Both the C++ BST implementation and the Net Income Calculator implementations were
developed using Test-driven Development using HUnit and QuickCheck, which helped
with understanding the requirements of the functions. The developer would without a
doubt utilize Test-driven Development again if there was a need for one of the
implementations. However, Net Income Calculator is a very rare exception where the
developer would use Test-driven Development. That is because it is not easy to design
9
the tests before the implementation is done, therefore, most of the implementations that
are relatively small and easy to implement should not utilize Test-driven Development,
since it would only waste the time of the developer by making the developer create
meaningless tests. As mentioned previously, Net Income Calculator is one of the rare
exceptions, because even though the implementation was relatively easy, it is mathheavy and requires high precision for it to function properly. For this reason, functional
programming seemed to be a very good programming paradigm for this project, as it is
based on the concept of many recursive mathematical functions instead of loops, which
made the development process efficient and the code bug-free and made the experience
enjoyable overall.
In terms of property-based testing, this project was the first time the developer has
utilized this concept and it did not seem too useful for the Net Income Calculator.
Therefore, critically evaluating property-based testing is out of scope in this instance.
However, it is evident that it can be very useful when creating tests for other
implementations.
To sum up, the developer will utilize Test-driven Development in the future for big
projects, or for projects that require high precision. It is a very suitable practice for the
industry since Test-driven Development promotes high-quality code in addition to
providing the tests prior to the code, aside from other benefits. However, for smaller
projects, the developer will stick with Test-last Development, as it only requires
additional work for something that is not entirely worth it, or for something that does not
take long to test manually after the implementation is complete.
References
Bhat, T. and Nagappan, N., 2006. Evaluating the efficacy of test-driven development.
[online] ACM Digital Library. Available at:
<https://dl.acm.org/doi/abs/10.1145/1159733.1159787> [Accessed 2 May 2022].
10
Agarwal, N. and Deep, P., 2014. Obtaining better software product by using test first
programming technique. [online] Ieeexplore.ieee.org. Available at:
<https://ieeexplore.ieee.org/document/6949233> [Accessed 2 May 2022].
Beck, K., 2003. Test-driven Development: By Example.
Shull, F., Melnik, G., Turhan, B., Layman, L., Diep, M. and Erdogmus, H., 2010. What Do
We Know about Test-Driven Development?. [online] Ieeexplore.ieee.org. Available at:
<https://ieeexplore.ieee.org/abstract/document/5604358> [Accessed 2 May 2022].
Janzen, D. and Saiedian, H., 2006. On the Influence of Test-Driven Development on
Software Design. [online] Ieeexplore.ieee.org. Available at:
<https://ieeexplore.ieee.org/abstract/document/1617340> [Accessed 2 May 2022].
Kollanus, S., 2010. Test-Driven Development - Still a Promising Approach?. [online]
Ieeexplore.ieee.org. Available at:
<https://ieeexplore.ieee.org/abstract/document/5655657> [Accessed 2 May 2022].
11
Download