2
\$\begingroup\$

I'm trying to write unit tests code for a java class. The class tests an array for sorted (either way) or not.

Here is the Unit Tests code:

public class SortedOrNotTest
{

   @Test
   public void test()
   {
      int[] A = { 32, 32, 32, 32, 32, 32, 32, 32, 34 };
      assertEquals(true, SortedOrNot.isSorted(A));

      int[] B = { 32, 32, 34, 33 };
      assertEquals(false, SortedOrNot.isSorted(B));

      int[] C = { 32 };
      assertEquals(true, SortedOrNot.isSorted(C));

      int[] D = {};
      assertEquals(true, SortedOrNot.isSorted(D));

      int[] E = { 32, 32, 31, 30, 32 };
      assertEquals(false, SortedOrNot.isSorted(E));
   }
}

Here is the Class, it checks an array is sorted or not in either way:

public class SortedOrNot
{

   public static boolean isSorted(int[] arr)
   {
      int n = arr.length;

      if(n<2) return true;

      int i = 0;
      while (i < n - 1 && arr[i] == arr[i + 1]) // same elements at the beginning
      {
         i++;
      }

      if (i == n - 1)
         return true;   // all same elements considered as sorted.

      if (arr[i] < arr[i + 1]) // candidate for ascending, non-decreasing
      {
         i++;

         for (; i < n - 1; i++)
         {
            if (arr[i] > arr[i + 1])
               return false;
         }

         return true;
      }

      else // candidate for descending, non-increasing
      {
         i++;

         for (; i < n - 1; i++)
         {
            if (arr[i] < arr[i + 1])
               return false;
         }
         return true;
      }
   }
}

Tests works, need to make sure that I'm doing in correct way.

\$\endgroup\$
  • \$\begingroup\$ Since you didn't include the include statements (in what was supposed to be a working code example), are we safe to assume that these are unit tests implemented using the JUnit testing framework? :) \$\endgroup\$ – TorbenPutkonen Oct 16 at 8:18
  • \$\begingroup\$ What are "include statements"? Yes, these tests were implemented using the JUnit testing framework. @TorbenPutkonen \$\endgroup\$ – Manoj Banik Oct 16 at 11:49
2
\$\begingroup\$

There isn't anything inherently wrong with repeating assertions in the same test, and for few test case values like yours this is perfectly fine. But if you need to test a large set of values, consider using a parameterized test like the following:

@RunWith(org.junit.runners.Parameterized.class)
public class SortedOrNotTest {

    @Parameterized.Parameter(0)
    public static boolean expectedResult;

    @Parameterized.Parameter(1)
    public static int[] array;

    @Parameterized.Parameters
    public static Object[][] test() {
        return new Object[][]{
                {true,  new int[] {32, 32, 32, 32, 32, 32, 32, 32, 34} },
                {false, new int[] {32, 32, 34, 33} },
                {true,  new int[] {32} },
                {true,  new int[] {} },
                {false, new int[] {32, 32, 31, 30, 32} }
        };
    }


    @Test
    public void paramTest() {
        assertEquals(expectedResult, SortedOrNot.isSorted(array));
    }
}
```
\$\endgroup\$
3
\$\begingroup\$

I don't see anything wrong with the way you are testing.

You may want to include negative numbers in your test cases.

I prefer to have test cases broken up. It's easier to track down failing tests:

@Test
public void testIsSortedSingleNode()
{
    int[] C = { 32 };
    assertEquals(true, SortedOrNot.isSorted(C));
}

Even in tests you should follow naming standards. Names such as A, B C are not very descriptive. Also they should start with a lower-case letter. result would be a better name. You use the same variable throughout.

Use assertTrue and assertFalse instead of assertEquals(true/false, x).

IMO arrayLength would be a better name then n. Or you could use arr.length.

\$\endgroup\$
2
\$\begingroup\$

Tests like these often involve a lot of copy-pasted boilerplate and that increases tremendously the effort required to maintain the tests. Instead of writing an assert statement for each array or a separate test method for each case, you could store the arrays into two lists; those that should be detected as sorted and those that should not be and process each array in a common test method.

private static final List<int[]> SHOULD_BE_SORTED = Arrays.asList(
            new int[] { Integer.MIN_VALUE, Integer.MAX_VALUE },
            new int[] { Integer.MIN_VALUE, Integer.MIN_VALUE },
            new int[] { Integer.MAX_VALUE, 1 },
            ...
            );

@Test
public void shouldBeSorted() {
    for (int i; i < SHOULD_BE_SORTED.size(); i++) {
        final int[] arr = SHOULD_BE_SORTED.get(i);
        final int[] orig = (int[]) arr.clone();

        assertTrue("Array " + i + " should have been sorted " + Arrays.toString(arr), 
            SortedOrNot.isSorted(arr));
        assertArrayEquals(orig, arr);
    }
}

Algorithms that deal with numbers should always be tested with the limits of the allowed value space. The algorithm must work with max and min values and fail with max+1 etc. When the numeric algorithm uses the natural limits of the primitive types, the tests must take into account possible overflows and the ways they may affect the calculations (e.g. adding one to Integer.MAX_VALUE making the result negative).

If the target code processes arrays and is not documented to modify the input, the tests should ensure that the array stays intact.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.