This article provides insufficient context for those unfamiliar with the subject. |
In computer science Behavior Driven Development is a programming technique that questions the behavior of an application before and during the development process. By asking questions as what should this application do? or what should this part do? developers can identify gaps in their understanding of the problem ___domain and talk to their peers or ___domain experts to find the answers.
The purpose is to question each part of the application and the application entirely. These questions deal not mere with technical or requirements issues but also with cost and time related issues. Is it feasible for our organization to build an application of this scope? is a question that can be answered by writing behavior tests. By exposing the complexities early on developers and management can make better assumptions on how fit the organization is to handle the creation of an application.
To prove the behavior of an application during and after the development process application code is tested through behavioral tests. These tests should answer the questions organizations have about the application and illustrate how an application works. When writing behavioral tests developers are assumed to solve the most important or critical questions first. Each test that is written deals with the next most important question on the list. If every question is solved the behavior of the application is defined in tests and the application has been created.
Behavioral tests
The examples below demonstrate how behavioral tests can be written using the Java programming language using the JUnit automated testing framework. This allows the tests to be executed swiftly and constantly during the development process.
The test below verifies if a prime number calculator can calculate the first prime number of 100 correctly.
public class PrimeNumberCalculatorTests extends junit.framework.TestCase { public void testIfPrimeAfter100Is101() { PrimeCalculator calculator = new EratosthenesPrimesCalculator(100); int result = calculator.nextPrime(); assertSame("First prime after 100 should be 101 but is " + result, 101, result); } }
The test above should be expanded so that more prime numbers are tested, as shown below:
public class PrimeNumberCalculatorTests extends junit.framework.TestCase { public void testIfPrimeAfter100Is101() { PrimeCalculator calculator = new EratosthenesPrimesCalculator(100); int result = calculator.nextPrime(); assertSame("First prime after 100 should be 101 but is " + result, 101, result); } public void testIfFirstPrimeIs2() { PrimeCalculator calculator = new EratosthenesPrimesCalculator(); int result = calculator.nextPrime(); assertSame("First prime should be 2 but is " + result, 2, result); } public void testIfPrimeAfter683Is691() { PrimeCalculator calculator = new EratosthenesPrimesCalculator(683); int result = calculator.nextPrime(); assertSame("First prime after 683 should be 691 but is " + result, 691, result); } public void testFirst10Primes() { PrimeCalculator calculator = new EratosthenesPrimesCalculator(); int[] primes = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31 }; for (int i = 0; i < primes.length; i++) { int result = calculator.nextPrime(); assertEquals("Expected prime: [" + primes[i] + "], got: [" + result "]", primes[i], result); } } }
Behavioral tests use the techniques of Test-Driven Development but have a more specific goal. The purpose is to define the behavior of an application rather than the implementation. In the example above it's not really important how the prime numbers are calculated - if calculated at all. What's important is that the numbers are correct which is the expected behavior of the application.
The application users may also require the prime number calculation must perform within certain benchmarks. If this is the case the behavioral tests should record the performance characterics and compare them with the expected results. But again this does not dictate how the prime number calculation should be implemented, it only dictates how it should behave.
Test conduct
When writing behavioral tests there are number of practical rules to keep in mind that will help developers to stay on track with the goals of testing behavior instead of implementation. These numbers aim to make both tests and application code and design as flexible and robust as possible.
- Test size: each test (that is each test method) should not be longer than 15 lines of code (not counting empty lines). Test methods that are longer are either too complex (or the implementation is inefficient) or test too much behavior.
- Mock objects: the usage of mock objects is encouraged (e.g. by using the EasyMock framework for Java) to achieve the previous goal.
- Helper methods: some behavior is best implemented in a helper method (e.g. load this or that from the database) that can be easily tested and called from elsewhere in the application. These methods help to make the code more expressive (through expressive method and argument names) and easier to test.
- No dependencies: behavioral tests should not depend on other test cases, the execution order of tests or test cases or the presences of any configured system.
- Isolated: behavioral tests should work in full isolation.
Specifically for Java there are two more rules:
- Packages: tests should sit in a separate source tree but in the same package to allow for better testing.
- Protected methods: helper methods that implemented behavior that can be tested should be declared with package-protected access to allow their invocation from test cases.
Test first, implement next
As promoted by most agile development techniques Behavior Driven Development promotes a short test/implement/improve cycle. Testing before implementating in a crucial aspect of Behavior Driven Development.
Test
The first step is to write a behavioral test, as shown below:
public class PrimeNumberCalculatorTests extends junit.framework.TestCase { public void testIfFirstPrimeIs2() { PrimeCalculator calculator = new EratosthenesPrimesCalculator(); int result = calculator.nextPrime(); assertSame("First prime should be 2 but is " + result, 2, result); } }
After writing the test the project should compile correctly. This means the PrimeCalculator
and EratosthenesPrimesCalculator
classes should exist and sould have a nextPrime()
method. Integrated Development Environment (IDE) tools can help with this by creating those classes and methods that are missing in the project. The code below is generated by an IDE:
public interface PrimeCalculator { int nextPrime(); } public class EratosthenesPrimesCalculator implements PrimeCalculator { public int nextPrime() { throw new UnsupportedOperationException(); } }
In the minds of the developers there may be methods missing from the classes above but since the behavioral test does not define other behaviors this is acceptable.
Implement
Once the behavioral test compiles the classes that are tested may be implemented. Their implementation is finished when the behavioral tests works without failing.
Improve
When the behavioral test works correctly developers may improve the implementation to work more efficiently. This can happen right after the implementation or at a later time.
Setting behavioral priorities
Developers, designers and project managers should talk with ___domain experts to determine what behaviors are the most crucial for the application to work properly and be useful for the organization. For each behavior it should be determined - or questioned - if sufficient information is available so that non-___domain experts - developers, designers, project managers - can test and implement it. It may not be apparent information is missing until testing starts.