junit-4.11.jar | |
File Size: | 245 kb |
File Type: | jar |
Here is just a small example about JUnit4, later I will take a few other Junit4 features. First let us check normal one.
First we create following Java class which will be tested later –
public class PrimeNumberChecker {
public PrimeNumberChecker(){
}
public Boolean validate(final Integer primeNumber) {
for (int i = 2; i < (primeNumber / 2); i++) {
if (primeNumber % i == 0) {
return false;
}
}
return true;
}
}
Following is the test class which will test the above class with the given parameters in the class definition as shown below.
Take special note of @RunWith() & @Parameterized
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Collection;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.BeforeClass;
import org.junit.Test;
import classes.PrimeNumberChecker;
@RunWith(Parameterized.class)
public class PrimeNumberCheckerTest {
private Integer inputNumber;
private Boolean expectedResult;
private static PrimeNumberChecker pnc;
@BeforeClass
public static void initialize(){
pnc = new PrimeNumberChecker();
}
//Each parameter should be placed as an argument here
// Every time runner triggers, it will pass the arguments
// from parameters we defined in primeNumbers() method
public PrimeNumberCheckerTest(Integer inputNumber, Boolean expectedResult) {
this.inputNumber = inputNumber;
this.expectedResult = expectedResult;
}
@Parameterized.Parameters
public static Collection primeNumbers() {
return Arrays.asList(new Object[][] {
{ 2, true },
{ 6, false },
{ 19, true },
{ 22, false },
{ 23, true }
});
}
@Test
public void test() {
System.out.println("Parameterized Number is : " + inputNumber);
assertEquals(expectedResult, pnc.validate(inputNumber));
}
}
Now let us create other JUnit classes. Below class is using snippets.TestClass which is nothing but a dummy class with one parameter having its getter & setter. So it is used here for the example only.
Other things to note here - @Ignore annotation, which is used to indicate that this class needs to be ignored while execution or ignore the method if that method is annotated with this. Below whole class will be ignored.
@BeforeClass annotation is used to execute a method only once before any test case is executed & it must be used with static methods only like shown below.
@Before, any method annotated with this will be executed everytime before any test case is executed.
Same way we have @AfterClass & @After.
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import snippets.TestClass;
@Ignore
public class TestJunit {
static TestClass tc;
@BeforeClass
public static void setter(){
tc = new TestClass();
}
@Before
public void initiate(){
tc.setName("Nitin");
}
@Test
public void testAdd()
{
System.out.println("In testAdd...");
String str= "Junit is working fine";
assertEquals("Junit is working fine",str);
}
// @Ignore
@Test
public void testName(){
System.out.println("In testName...");
assertEquals("Names are not equal", "Nitin1", tc.getName());
}
}
import static org.junit.Assert.*;
import org.junit.Test;
public class TestJunit1 {
@Test
public void testAdd()
{
String str= "Junit is working fine";
assertEquals("Junit is working fine",str);
}
}
Now we create the test suite to execute multiple test classes during the one run -
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import testCases.TestJunit;
import testCases.TestJunit1;
@RunWith(Suite.class)
@Suite.SuiteClasses({
TestJunit1.class,
TestJunit.class
})
public class JunitTestSuite {
}
Well, nothing is required to be there in the class as every fuction required for the test suite is covered by the annotations above - @RunWith & @Suite.SuiteClasses. Creation of the empty class is just a place for the annotations to sit. Execute the test suite & check the output to see which test cases or test classes are executed.
Now if you want to execute a JUnit class like a normal java file & also get the results along with failure messages & what test cases got passed then it can easily be achieved by creation of TestRunner like shown below -
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
import suites.JunitTestSuite;
import testCases.PrimeNumberCheckerTest;
public class TestRunner {
public static void main(String[] args)
{
Result result = JUnitCore.runClasses(JunitTestSuite.class, PrimeNumberCheckerTest.class);
// Result result = JUnitCore.runClasses(PrimeNumberCheckerTest.class);
for (Failure failure : result.getFailures())
{
System.out.println(failure.toString());
}
System.out.println(result.wasSuccessful());
}
}
Check the execution results of the above file.
Like above we used @Test only but we can use its other feature depending on the requirements like -
1) If you expect some method to be executed within certain time then we can use @Test(timeout = 1000). Here 1000 is in ms i.e. milliseconds.
So if we use @Test with above shown parameter then the JUnit will show failure if the method is not executed with the time mentioned.
2) If you expect some exception to be thrown by the method under test then we can use @Test(expected=ArithmeticException.class)
So if the method under test don't throw the mention exception for the data passed to it then JUnit will fail.
Check also the SOP statements placed in above test cases block. Try to put these statements after assert statements & your SOP statements will not get executed as control returns once the assert statement is executed.
Now another important factor missed while writing the test cases & selecting a particular JUnit version. Always keep in mind that if you are using the JDV 6 or below that & have written the test cases expecting them to be run in the order written in the JUnit class then it can work & your cases may be executed in the same order everytime but since JDk 7 it has changed, you can no longer guarantee that the order of methods returned by reflection is the same as the order in which they are declared in the source file. Actually, this was never guaranteed, but most JVMs did return the methods in source file order. And all of them did return them in a consistent order. With Java 7, even this is no longer guaranteed. The order that the methods are returned can vary from run to run.
So why did JUnit care about this? JUnit finds the tests that it runs using reflection. And the tests are run in this order. So, if a test suite has implicit or explicit dependencies between tests, a test run can sometimes succeed and other times fail.
So, using the following test case as an example:
public class ExecutionOrderTest { @Test public void firstTest() { System.out.println("firstTest"); } @Test public void secondTest() { System.out.println("secondTest"); } @Test public void thirdTest() { System.out.println("thirdTest"); } public static void main(String[] args) { JUnitCore.runClasses(ExecutionOrderTest.class); } }
Using java 1.6 & 4.10, we get:
firstTest
secondTest
thirdTest
Whereas with java 1.7 we get:
thirdTest
firstTest
secondTest
So the order is different. So, what’s the fix? After a lot of discussion, it was decided to make the sort order of methods deterministic, but unpredictable. So, we still get the tests in a strange order, but at least the next time we run the tests, we’ll still get the same order, which makes debugging a lot easier.
However, even with this, there is still a problem. The algorithm used to calculate the deterministic order is based on the hashCode of the method name, it’s pretty obscure. This means that if I have a problem with ordering then I can’t easily fix it. For instance, the hashCode of “secondTest” is 423863078 and “thirdTest” is -585354599. Which means that thirdTest will be executed before secondTest. But if I want for whatever reason to execute thirdTest after secondTest, I have to rename thirdTest to something with a hashCode of greater than 423863078. Yuck. But, there is a solution.
@FixMethodOrder(MethodSorters.NAME_ASCENDING) public class ExecutionOrderTest { @Test public void firstTest() { System.out.println("firstTest"); } @Test public void secondTest() { System.out.println("secondTest"); } @Test public void thirdTest() { System.out.println("thirdTest"); } public static void main(String[] args) { JUnitCore.runClasses(ExecutionOrderTest.class); } }
@FixMethodOrder in JUnit4.11 allows the developer to specify that in this case, please execute the tests in order of name ascending. So this will at least allow me to fix the order of my broken (see below) tests. There are three possible values we can specify:
http://randomallsorts.blogspot.in/2012/12/junit-411-whats-new-test-execution-order.html
Following are few important JUnit extensions
Cactus
JWebUnit
XMLUnit
MockObject
First we create following Java class which will be tested later –
public class PrimeNumberChecker {
public PrimeNumberChecker(){
}
public Boolean validate(final Integer primeNumber) {
for (int i = 2; i < (primeNumber / 2); i++) {
if (primeNumber % i == 0) {
return false;
}
}
return true;
}
}
Following is the test class which will test the above class with the given parameters in the class definition as shown below.
Take special note of @RunWith() & @Parameterized
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Collection;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.BeforeClass;
import org.junit.Test;
import classes.PrimeNumberChecker;
@RunWith(Parameterized.class)
public class PrimeNumberCheckerTest {
private Integer inputNumber;
private Boolean expectedResult;
private static PrimeNumberChecker pnc;
@BeforeClass
public static void initialize(){
pnc = new PrimeNumberChecker();
}
//Each parameter should be placed as an argument here
// Every time runner triggers, it will pass the arguments
// from parameters we defined in primeNumbers() method
public PrimeNumberCheckerTest(Integer inputNumber, Boolean expectedResult) {
this.inputNumber = inputNumber;
this.expectedResult = expectedResult;
}
@Parameterized.Parameters
public static Collection primeNumbers() {
return Arrays.asList(new Object[][] {
{ 2, true },
{ 6, false },
{ 19, true },
{ 22, false },
{ 23, true }
});
}
@Test
public void test() {
System.out.println("Parameterized Number is : " + inputNumber);
assertEquals(expectedResult, pnc.validate(inputNumber));
}
}
Now let us create other JUnit classes. Below class is using snippets.TestClass which is nothing but a dummy class with one parameter having its getter & setter. So it is used here for the example only.
Other things to note here - @Ignore annotation, which is used to indicate that this class needs to be ignored while execution or ignore the method if that method is annotated with this. Below whole class will be ignored.
@BeforeClass annotation is used to execute a method only once before any test case is executed & it must be used with static methods only like shown below.
@Before, any method annotated with this will be executed everytime before any test case is executed.
Same way we have @AfterClass & @After.
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import snippets.TestClass;
@Ignore
public class TestJunit {
static TestClass tc;
@BeforeClass
public static void setter(){
tc = new TestClass();
}
@Before
public void initiate(){
tc.setName("Nitin");
}
@Test
public void testAdd()
{
System.out.println("In testAdd...");
String str= "Junit is working fine";
assertEquals("Junit is working fine",str);
}
// @Ignore
@Test
public void testName(){
System.out.println("In testName...");
assertEquals("Names are not equal", "Nitin1", tc.getName());
}
}
import static org.junit.Assert.*;
import org.junit.Test;
public class TestJunit1 {
@Test
public void testAdd()
{
String str= "Junit is working fine";
assertEquals("Junit is working fine",str);
}
}
Now we create the test suite to execute multiple test classes during the one run -
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import testCases.TestJunit;
import testCases.TestJunit1;
@RunWith(Suite.class)
@Suite.SuiteClasses({
TestJunit1.class,
TestJunit.class
})
public class JunitTestSuite {
}
Well, nothing is required to be there in the class as every fuction required for the test suite is covered by the annotations above - @RunWith & @Suite.SuiteClasses. Creation of the empty class is just a place for the annotations to sit. Execute the test suite & check the output to see which test cases or test classes are executed.
Now if you want to execute a JUnit class like a normal java file & also get the results along with failure messages & what test cases got passed then it can easily be achieved by creation of TestRunner like shown below -
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
import suites.JunitTestSuite;
import testCases.PrimeNumberCheckerTest;
public class TestRunner {
public static void main(String[] args)
{
Result result = JUnitCore.runClasses(JunitTestSuite.class, PrimeNumberCheckerTest.class);
// Result result = JUnitCore.runClasses(PrimeNumberCheckerTest.class);
for (Failure failure : result.getFailures())
{
System.out.println(failure.toString());
}
System.out.println(result.wasSuccessful());
}
}
Check the execution results of the above file.
Like above we used @Test only but we can use its other feature depending on the requirements like -
1) If you expect some method to be executed within certain time then we can use @Test(timeout = 1000). Here 1000 is in ms i.e. milliseconds.
So if we use @Test with above shown parameter then the JUnit will show failure if the method is not executed with the time mentioned.
2) If you expect some exception to be thrown by the method under test then we can use @Test(expected=ArithmeticException.class)
So if the method under test don't throw the mention exception for the data passed to it then JUnit will fail.
Check also the SOP statements placed in above test cases block. Try to put these statements after assert statements & your SOP statements will not get executed as control returns once the assert statement is executed.
Now another important factor missed while writing the test cases & selecting a particular JUnit version. Always keep in mind that if you are using the JDV 6 or below that & have written the test cases expecting them to be run in the order written in the JUnit class then it can work & your cases may be executed in the same order everytime but since JDk 7 it has changed, you can no longer guarantee that the order of methods returned by reflection is the same as the order in which they are declared in the source file. Actually, this was never guaranteed, but most JVMs did return the methods in source file order. And all of them did return them in a consistent order. With Java 7, even this is no longer guaranteed. The order that the methods are returned can vary from run to run.
So why did JUnit care about this? JUnit finds the tests that it runs using reflection. And the tests are run in this order. So, if a test suite has implicit or explicit dependencies between tests, a test run can sometimes succeed and other times fail.
So, using the following test case as an example:
public class ExecutionOrderTest { @Test public void firstTest() { System.out.println("firstTest"); } @Test public void secondTest() { System.out.println("secondTest"); } @Test public void thirdTest() { System.out.println("thirdTest"); } public static void main(String[] args) { JUnitCore.runClasses(ExecutionOrderTest.class); } }
Using java 1.6 & 4.10, we get:
firstTest
secondTest
thirdTest
Whereas with java 1.7 we get:
thirdTest
firstTest
secondTest
So the order is different. So, what’s the fix? After a lot of discussion, it was decided to make the sort order of methods deterministic, but unpredictable. So, we still get the tests in a strange order, but at least the next time we run the tests, we’ll still get the same order, which makes debugging a lot easier.
However, even with this, there is still a problem. The algorithm used to calculate the deterministic order is based on the hashCode of the method name, it’s pretty obscure. This means that if I have a problem with ordering then I can’t easily fix it. For instance, the hashCode of “secondTest” is 423863078 and “thirdTest” is -585354599. Which means that thirdTest will be executed before secondTest. But if I want for whatever reason to execute thirdTest after secondTest, I have to rename thirdTest to something with a hashCode of greater than 423863078. Yuck. But, there is a solution.
@FixMethodOrder(MethodSorters.NAME_ASCENDING) public class ExecutionOrderTest { @Test public void firstTest() { System.out.println("firstTest"); } @Test public void secondTest() { System.out.println("secondTest"); } @Test public void thirdTest() { System.out.println("thirdTest"); } public static void main(String[] args) { JUnitCore.runClasses(ExecutionOrderTest.class); } }
@FixMethodOrder in JUnit4.11 allows the developer to specify that in this case, please execute the tests in order of name ascending. So this will at least allow me to fix the order of my broken (see below) tests. There are three possible values we can specify:
- MethodSorters.JVM: the order in which the methods are returned by the JVM, potentially different order each time
- MethodSorters.DEFAULT: deterministic ordering based upon the hashCode
- MethodSorters.NAME_ASCENDING: order based upon the lexicographic ordering of the names of the tests.
http://randomallsorts.blogspot.in/2012/12/junit-411-whats-new-test-execution-order.html
Following are few important JUnit extensions
Cactus
JWebUnit
XMLUnit
MockObject
Not writting much here, just saying that we all use loggers in our code to log various events in the log file.
We test the working of our application methods only, but what about the logs which are are writting there.
Means, it will be good to test the log statements also, if the required logs are being logged or not, during testing, isn' it?
So check some ideas on -
java - How to intercept SLF4J (with logback) logging via a JUnit test? - Stack Overflow
Testing Your SLF4J Logs · Jamie Tanna | Software Engineer (jvt.me)
We test the working of our application methods only, but what about the logs which are are writting there.
Means, it will be good to test the log statements also, if the required logs are being logged or not, during testing, isn' it?
So check some ideas on -
java - How to intercept SLF4J (with logback) logging via a JUnit test? - Stack Overflow
Testing Your SLF4J Logs · Jamie Tanna | Software Engineer (jvt.me)
Check Awaitaility to test async APIs