Apache Airavata

A natural instinct for a developer is to write more code and less tests and no documentation. But the pay off is opposite. Here we emphasize the role of tests and suggested development methodology for Apache Airavata.

Why tests are important ?

The importance of tests clearly shows up for a complex project like Airavata and more importantly during code maintenance. During active development, you may undermine the importance of tests and feel lazy to implement tests. Whenever we write code we have to maintain them. It's not that we just write and throw them away. So when doing modifications to your code you tend to focus on your modification rather than on the overall functionality of the module (In fact it is not practical to memorize module functionalities in your head). Then you tend to forget what functionalities got affected due to your change. This may lead to a crisis !. So you can alleviate such situations if you have a good test coverage. This gets even more important as you scale up your contributions.

More reasons why we need to have tests

1. It shapes-up your code - When we have tests we tend to write code so that each unit is individually testable. This leads us to have properly and nicely parameterized interfaces in our methods and in our classes.
2. Increases stability
3. Allows third party reader to easily understand your code and experiment with a specific piece of the code rather than executing whole program. - This make life of contributors much easier.

In Airavata we follow Test Driven Development (TDD) approach. We will explain what is TDD in "Writing Unit Tests" section.

Tests are mainly categorize into two levels in Airavata.

1. Unit tests
2. Integration tests

Writing Unit Tests

For writing unit tests we follow TDD approach.

What is TDD and how we use it in Airavata ?

TDD is the practice of developing the test before you actually implement the logic and refine it while you complete the logic.

In the following we summarize the steps you have to follow when using TDD

1. You may wonder how are we going to write unit tests without implementing the actual logic. When implementing a programming solution for a particular problem we first analyze it and come up with a class structure. In TDD we first write method signatures without body. For example let's say we need to implement a method to add 2 numbers.

Adder.java
package org.apache.airavata.tdd.sample;

public class Adder {
 
  public int add (int n1, int n2) {
     return 0; // <- this is not the correct logic, but we put some value so that code is compilable
  }

}

2. Now we write the test,

AdderTest.java
package org.apache.airavata.tdd.sample;

import junit.framework.TestCase;

public class AdderTest extends TestCase {

  private Adder adder;

  public void setUp() {
     adder = new Adder();
  }

  public int testAdd () {
     Assert.assertEqual(10, adder.add(8, 2), "Expected values does not match evaluated value");
  }

}

So if we do a "mvn clean install" in adder module we will get a test failure. This says our test is working !

3. Now we modify our code to implement the actual logic.

i.e

Adder.java
package org.apache.airavata.tdd.sample;

public class Adder {
 
  public int add (int n1, int n2) {
     return (n1 + n2);
  }

}

This time if we do a "mvn clean install" our build will be successful.

You may observe that when we finish implementing the logic we have also completed implementing test case which verifies our logic.

4. As the last step you can refactor your code to improve its code quality. You confidently refactor code because now you have test cases which will make sure the correctness of your code.

Some conventions

1. We use Junit to implement unit tests
2. If the class implementing logic is "A", name test class as "ATest".
3. Package name of the actual class and test class should be the same
4. With tests module, structure should look as follows,

src
 |
 -- main
 -- test
     |
     -- java
        |
        ...
     -- resources

5. If tests take any parameters such as port number during execution, put them as maven "system property variables".

Integration Tests

During Airavata build, we run integration tests as the last step. i.e. Integration Tests run after all distributions are created.

Integration tests reside in <source branch>/moduels/integration-tests.

Within integration tests we extract distributions and deploy them in a tomcat server. Then we start server programmatically and run tests against the started server. We use TestNG for integration tests.

The main class which does most of the preparations for integration tests is

org.apache.airavata.integration.BaseCaseIT

.
To add more integration tests, you can add test methods to this class or you can extend this class and add new test methods.

At the start of each test method, you can assume server is started.

A sample test method would look like follows;

AdderTest.java
    @Test(groups = { "setupTests" }, dependsOnMethods = { "testSetup" })
    public void testURLs() throws AiravataAPIInvocationException {
        URI eventingServiceURL = this.airavataAPI.getAiravataManager().getEventingServiceURL(); // *At this point we know server is started*
        Assert.assertNotNull(eventingServiceURL);

        URI messageBoxServiceURL = this.airavataAPI.getAiravataManager().getMessageBoxServiceURL();
        Assert.assertNotNull(messageBoxServiceURL);
    }
org.apache.airavata.integration.BaseCaseIT

also provides some utility method to get running port, server URL, etc ...

These kind of tests will stop making you indispensable which will allow you to innovate more and still make you very valuable.
Feel free to add more tests and do improvements to the framework.

  • No labels