In the Loklak Server project, we use a number of automation tools like the build testing tool ‘TravisCI’, automated code reviewing tool ‘Codacy’, and ‘Gemnasium’. We are also using JUnit, a java-based unit-testing framework for writing automated Unit-Tests for java projects. It can be used to test methods to check their behaviour whenever there is any change in implementation. These unit-tests are handy and are coded specifically for the project. In the Loklak Server project it is used to test the web-scrapers. Generally JUnit is used to check if there is no change in behaviour of the methods, but in this project, it also helps in keeping check if the website code has been modified, affecting the data that is scraped.
Let’s start with basics, first by setting up, writing a simple Unit-Tests and then Test-Runners. Here we will refer how unit tests have been implemented in Loklak Server to familiarize with the JUnit Framework.
Setting-UP
Setting up JUnit with gradle is easy, You have to do just 2 things:-
1) Add JUnit dependency in build.gradle
Dependencies { . . . . . .<other compile groups>. . . compile group: 'com.twitter', name: 'jsr166e', version: '1.1.0' compile group: 'com.vividsolutions', name: 'jts', version: '1.13' compile group: 'junit', name: 'junit', version: '4.12' compile group: 'org.apache.logging.log4j', name: 'log4j-1.2-api', version: '2.6.2' compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.6.2' . . . . . . }
2) Add source for ‘test’ task from where tests are built (like here).
Save all tests in test directory and keep its internal directory structure identical to src directory structure. Now set the path in build.gradle so that they can be compiled.
sourceSets.test.java.srcDirs = ['test']
Writing Unit-Tests
In JUnit FrameWork a Unit-Test is a method that tests a particular behaviour of a section of code. Test methods are identified by annotation @Test.
Unit-Test implements methods of source files to test their behaviour. This can be done by fetching the output and comparing it with expected outputs.
The following test tests if twitter url that is created is valid or not that is to be scraped.
/** * This unit-test tests twitter url creation */ @Test public void testPrepareSearchURL() { String url; String[] query = {"fossasia", "from:loklak_test", "spacex since:2017-04-03 until:2017-04-05"}; String[] filter = {"video", "image", "video,image", "abc,video"}; String[] out_url = { "https://twitter.com/search?f=tweets&vertical=default&q=fossasia&src=typd", "https://twitter.com/search?f=tweets&vertical=default&q=from%3Aloklak_test&src=typd", "and other output url strings to be matched…..." }; // checking simple urls for (int i = 0; i < query.length; i++) { url = TwitterScraper.prepareSearchURL(query[i], ""); //compare urls with urls created assertThat(out_url[i], is(url)); } // checking urls having filters for (int i = 0; i < filter.length; i++) { url = TwitterScraper.prepareSearchURL(query[0], filter[i]); //compare urls with urls created assertThat(out_url[i+3], is(url)); } }
Testing the implementation of code is useless as it will either make code more difficult to change or tests useless . So be cautious while writing tests and keep difference between Implementation and Behaviour in mind.
This is the perfect example for a simple Unit-Test. As we see there are some points, which needs to be observed like:-
1) There is a annotation @Test .
2) Input array of query which is fed to the method TwitterScraper.prepareSearchURL() .
3) Array of urls out_url[], which are the expected urls to output.
4) asserThat() to compare the expected url (in array out_url[]) and the output url (in variable ‘url’).
NOTE: assertEquals() could also be used here, but we prefer to use assert methods to get error message that is readable (We will discuss about this some time later)
And the TestRunner
When we are working on a project, It is not feasible to run tests using gradle as they are first built (else verified whether tests are build-ready) and then executed. gradle test shall be used only for building and testing the tests. For testing the project, one shall set-up TestRunner. It allows to run specific set of tests, one wants to run.
TestRunners are built once using gradle (with other tests) and can be run whenever you want. Also it is easy to stack up the test classes you want to run in SuiteClasses and @RunWith to run SuiteClasses with the TestRunner.
In loklak server, TestRunner runs the web-scraper tests. They are used by developers to test the changes they have made.
This is a sample TestRunner, code link here .
package org.loklak; // Library classes imported import org.junit.runner.RunWith; import org.junit.runners.Suite; // Source files to be tested import org.loklak.harvester.TwitterScraperTest; import org.loklak.harvester.YoutubeScraperTest; /* * TestRunner for harvesters */ @RunWith(Suite.class) @Suite.SuiteClasses({ TwitterScraperTest.class, YoutubeScraperTest.class }) public class TestRunner { }
You can also add TestRunners for different sections of the project. Like here it is initialized only to test harvesters.
To run the TestRunner
Add classpath of the jar file of the project and run ‘JUnitCore’ with TestRunner to get output on terminal.
java -classpath .:build/libs/<yourProject>.jar:build/classes/test org.junit.runner.JUnitCore org.loklak.TestRunner
In the project we have set up a shell script to run the tests.
Few points
1) Build the project and tests separately. Build tests only when changed as they take time to be built and executed.
2) Whenever you are done with the coding part, run the tests using TestRunner.
3) Write unit-tests whenever you add a new feature to the project to keep it up-to-date.
Now lets end up here.
So for now, Code it, Test it and Repeat.
Resources:
- Loklak Server Tests directory: https://github.com/loklak/loklak_server/tree/development/test
- JUnit: http://junit.org/junit4/
- Junit TestRunner (further): http://www.codeaffine.com/2014/09/03/junit-nutshell-test-runners/