开发者

Unit testing simple word function

I am new to unit testing and when reading about it I am getting confused about how to do it.

I have this code which shuffles a word entered:

public static void shuffle(String word) {

    // Store string into array
        ArrayList<Character> wordShuffled = new ArrayList<Character>();
        // loop the index of the string
        for (i开发者_如何学JAVAnt i = 0; i < wordE.length(); i++) {
            wordShuffled.add(word.charAt(i));// add the word
        }
        Collections.shuffle(wordShuffled);// shuffle the word

How can I write unit tests for the code above. Thanks


An easy check would be to create a hashmap of character vs frequency for the original word.

For ex. if your word is "Doppelganger", the map would be

D->1
o->1
p->2
e->2
l->1
g->2
a->1
n->1
r->1

Create a similar map for the shuffled word. The two hashmaps should be equal.

This, however, will only check that the shuffled word contains the same letters as the original word. You should also check that the word is actually shuffled by checking for String equality and running the shuffle multiple times as @Shengyuan has pointed out.


The obvious Pass test is:

Arrange: create a known word.

Act: call shuffle.

Assert: check result is a permutation of original word.

Then look for failure points or boundary cases for further tests.


A couple of problems that make this piece of code hard to test are:

  1. Your function shuffle is declared static
  2. The function calls Collections.shuffle, making it really hard to mock

Here are some suggestions as to how you could test this method, albeit they would require some changes to your design:

public class WordUtil {
    public void shuffle(String word) {

        // Store string into array
        // -> It is better to code to the interface, eg, List, Set, Map 
        // than to the implementation, eg. ArrayList, LinkedList, HashMap, etc.
        List<Character> wordShuffled = new ArrayList<Character>();
        // loop the index of the string
        for (int i = 0; i < wordE.length(); i++) {
            wordShuffled.add(word.charAt(i));// add the word
        }
       Collections.shuffle(wordShuffled);// shuffle the word
    }
    // we are making this method visible only for testing purposes but 
    // shouldn't be regarded as public API
    void shuffle(Collection c) {
        Collections.shuffle(c);
    }
}

public class WordUtilTest {
    private boolean shuffleForCollectionWasCalled;
    private Collection collectionForShuffle;

    public void testShuffle() throws Exception {
        WordUtil util = new WordUtil_ForTest();
        String word = "some word";

        util.shuffle(word);

        assertTrue(shuffleForCollectionWasCalled);
        List<Character> expected = new ArrayList<Character>();
        for (int i = 0; i < word.length; i++) {
            expected.add(word.charAt(i);
        }
        assertEquals(expected, collectionForShuffle);
    }

    private static class WordUtil_ForTest extends WordUtil {

        @Override
        void shuffle(Collection c) {
            shuffleForCollectionWasCalled = true;
            collectionForShuffle = c;
        }
    }
}

While this might seem a bit convoluted, we need to introduce the void shuffle(Collection c) because it allows us to control the behavior of Collections.shuffle making it deterministic, and the implementation we have in WordUtil_ForTest is trivial because we are not testing how Collections.shuffle does its work (this is supposed to be well tested in the JDK) we just care that we send the right parameters to it.

You can extend this test by adding conditions to handle null inputs and other edge cases.


Compare results of shuffle() twice, assertFail() if results are same.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜