4
public static String generateSaltString() {
    SecureRandom random = new SecureRandom();
    byte[] salt = random.generateSeed(12);

    return byteToBase64(salt);
}

Wondering how to write test case using Junit for this method. Any suggestion?

Anuj Balan
  • 7,629
  • 23
  • 58
  • 92
johnnily
  • 153
  • 2
  • 5
  • 11
  • possible duplicate of [Unit testing a method with random behaviour](http://stackoverflow.com/questions/3812154/unit-testing-a-method-with-random-behaviour) – guerda Apr 16 '13 at 18:26
  • 1
    @guerda That article is about *how to deal with randomness*. The question here is *how to test whether constrained random value is generated or not* – gaborsch Apr 16 '13 at 19:43

4 Answers4

3

First thing is that new SecureRandom() can be potentially very slow so you might want to cache it. Look at this answer and this one too.

I would refactor your code as follows

public class DoRandomStuff {

private RandomUtil randomUtil;

public DoRandomStuff(RandomUtil randomUtil) {
    this.randomUtil = randomUtil;
}

public String generateSaltString() {
    byte[] salt = randomUtil.generateSeed(12);
    return byteToBase64(salt);
}

public String byteToBase64(byte[] salt) {
    // Do salt generation logic here
    return null;
}

generateSeed looks like a utility method call so it can go into a class RandomUtil which looks like this

class RandomUtil {
    private SecureRandom random = new SecureRandom();
    public byte[] generateSeed(int seed) {
        return random.generateSeed(seed);
    }
}

Now your test class for DoRandomStuff would be easy to write. Inject a mocked randomUtil using some testing framework like Mockito. Make the mockedRandomUtil.generateSeed(int) method return a fixed number. Your test is really for checking whether byteToBase64() method is doing what its supposed to do. Now you have a deterministic number for your test case. You can give all kinds of numbers which can be generated by SecureRandom in your test class as separate test cases to check byteToBase64() result. As a plus your random number generation code is now decoupled from your base64 code.

Hope it helped.

Community
  • 1
  • 1
1

i would test if the result is not null and then call the method several times and compare the results to show that each call returns a different value.

But remember: This says nothing about the quality of the randomness of your results!

@Test
public void testGenerateSaltString() {
    String salt1 = YourClass.generateSaltString();
    String salt2 = YourClass.generateSaltString();
    String salt3 = YourClass.generateSaltString();
    String salt4 = YourClass.generateSaltString();

    assertNotNull(salt1);
    assertNotNull(salt2);
    assertNotNull(salt3);
    assertNotNull(salt4);

    assertNotEqual(salt1, salt2);
    assertNotEqual(salt1, salt3);
    assertNotEqual(salt1, salt4);

    assertNotEqual(salt2, salt3);
    assertNotEqual(salt2, salt4);

    assertNotEqual(salt3, salt4);
}

Considering GaborSch's comments i thought of a somewhat fuzzy implementation of the test since it is possible - though unlikely - that two calls of the function will generate the same salt:

@Test
public void testGenerateSaltString() {
    String salt1;
    String salt2;
    int differenceCount = 0;

    for(int i = 0; i < 1000; i++ ) {
        String salt1 = YourClass.generateSaltString();
        String salt2 = YourClass.generateSaltString();

        // null is still inacceptable
        assertNotNull(salt1);
        assertNotNull(salt2);

        if(!salt1.equalsIgnoreCase(salt2)) {
            differenceCount++;
        }
    }

    // check if at least at 95% of all tries resultet in different strings
    // change this value according to your needs
    assertTrue(differenceCount >= 950);
}
Marco Forberg
  • 2,634
  • 5
  • 22
  • 33
  • You could improve it a bit, doing a series of 4-value tests. It is possible, that there will be 2 equals here, but it is unlikely that if you do 5 of this 4-value tests, more than one will produce a matching pair. – gaborsch Apr 16 '13 at 19:48
  • *frown* where do you see the possibility for matching pairs? – Marco Forberg Apr 16 '13 at 19:57
  • Theoretically it has the same chance to throw `6,6,6,6,6,6` with a dice as to throw `1,5,3,2,6,5`. So, there is a chance for that. You cannot predict, what value is coming, that's why it is called *random*. So, you even **cannot exclude** that the same values will come in a row. – gaborsch Apr 16 '13 at 20:02
  • ah okay you meant matching results. thought you meant i forgot an equality check. but i see your point. this is one of the things why i'm looking for some advices on how to "test" *randomness* – Marco Forberg Apr 16 '13 at 20:04
  • 1
    Only by probabilities. You can give a confidence range to the event that the given method is random. Throwing 6 times the same value sometimes happens, both throwing 6000 times is *very* unlikely. So, given a result, you can decide whether the function produces random or not. – gaborsch Apr 16 '13 at 20:11
  • after sleeping over it i added a "fuzzy" version of the test – Marco Forberg Apr 17 '13 at 03:02
0

I can think of two test cases -

  • Check that the return is not null.
  • Check that the return is base64 if you are expecting it to be base64. Although it does not matter if you are expecting any random String.
Bhushan Bhangale
  • 10,921
  • 5
  • 43
  • 71
0

You can check 3 things:

  1. If the result is null or not
  2. If the result meets the formal requirements (is a Base64 String)
  3. If the result is random enough

For example, the following code could work. I generate 100 different random salts, and allow maximum 1 match among them.

@Test
public void testGenerateSaltString() {

    Set<String> salts = new HashSet<String>();
    int countSame = 0;
    BASE64Decoder decoder = new BASE64Decoder();

    for(int i=0; i<100; i++) {
        String salt = YourClass.generateSaltString();
        assertNotNull(salt);
        try {
            decoder.decodeBuffer(encodedBytes);
       } catch (Exception e) {
           fail("Not Base64");
       }
       if (salts.contains(salt)) {
            sameCount++;
       }
       salts.add(salt);
    }

    assertTrue(countSame <= 1);
}
gaborsch
  • 15,408
  • 6
  • 37
  • 48