3

I have String method with no arguments, but it having if conditions.

public String id1()
{
    Random random=new Random();
    int i=random.nextInt();
    if(i<0)
    {
        i=-(i);
    }
    if(i<1000)
        i=i+1000;
    while(i>9999)
    {
        i=i/10;
    }
    return i+"";
}

How can I write a a Junit test case in this situation?

Box Box Box Box
  • 5,094
  • 10
  • 49
  • 67

3 Answers3

3

You could break the logic into its own method:

public String id1(int i)
{
    if(i<0)
    {
        i=-(i);
    }
    if(i<1000)
        i=i+1000;
    while(i>9999)
    {
        i=i/10;
    }
    return i+"";
}

Then change the way you call id1 by just passing it a random int:

id1(new Random().nextInt());

Then you can test the logic. This is the easiest, if not the most perfect, solution. The more difficult solution is to mock the nextInt() method so you control what it returns inside your test - probably not necessary here, but for some situations that will be the only way.

nhouser9
  • 6,730
  • 3
  • 21
  • 42
2

Something like this. Your test class:

import java.util.Random;

public class Simple {
    public String id1()
    {
        Random random=new Random();
        int i=random.nextInt();
        if(i<0)
        {
            i=-(i);
        }
        if(i<1000)
            i=i+1000;
        while(i>9999)
        {
            i=i/10;
        }
        return i+"";
    }
}

Tests(I use powermock-mockito-1.6.2). You should manipulate generated number:

import java.util.Random;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest({ Random.class, Simple.class })
public class SimpleTest {

    @Test
    public void ifGenerateNumberLessZeroSetItMoreThanZero() throws Exception {
        Random rand = Mockito.mock(Random.class);
        PowerMockito.mockStatic(Random.class);
        PowerMockito.whenNew(Random.class).withNoArguments().thenReturn(rand);

        Mockito.when(rand.nextInt()).thenReturn(-9999);

        Simple simple = new Simple();
        Assert.assertEquals("9999", simple.id1());
    }
}
Evgeniy K.
  • 1,137
  • 7
  • 11
  • Is this what's called reflection? – David Findlay Jun 29 '16 at 04:20
  • 2
    Seriously: don't follow this advise. Your problem is that you created untestable code; and using PowerMock will cause much more trouble ... than simply going and **fixing** your broken design. – GhostCat Jun 29 '16 at 06:45
  • 1
    @DavidFindlay PowerMock is **not** using reflection. It is a mocking framework that simply takes compiled class files and **changes** the bytecode in there to make things testable. That is why the number of "good use cases" for PowerMock ... is very close to zero. – GhostCat Jun 29 '16 at 07:00
0

Your problem is that you created untestable code.

The point is: if a method creates the input it is working on itself ... then there is simply no testing that method. Yes, you could use PowerMock to gain control over the random class, but that is the completely wrong approach. PowerMock is simply something that nobody should be using (see here for some reasons behind that); if at all, you use it for 3rd party code that you can't change. But when we are talking about your own code, then it is much better to come up with testable designs; instead of creating a bad design, to then turn to PowerMock to somehow test it.

In general, you use dependency injection in order to gain control over the "data" that your methods will be working with; in this case, that could be as simple as:

public String formatStringIdFrom(int id) { ... }

If you really want to improve the quality of your code, I recommend you to step back for some 3, 5 hours and watch all the videos from this google tech series ... believe me, it is absolutely worth your time.

Some other notes:

  • The essence of good unit tests are ... they always return the same result. This means: code that is based on random values ... should at least allow for providing a seed so that you can write test cases that are guaranteed to always see the same input. Unit tests that give you different results in each run ... are simply: not very helpful.
  • id1() is a pretty useless name for a method. It doesn't tell you anything about what method is supposed to do
  • Consider creating a concrete class that really represents IDs. Good design is about creating abstractions that allow you to deal with things in a more meaningful way. Just pushing around integers; and declaring that any 4-digit string made from such an integer is an "id" ... is simply a very naive approach.
  • Finally: you can dramatically simplify the generation of 4 digit numbers; you only need: int number = random.nextInt(9000) + 1000 directly gives you values between (1000, 9999).
Community
  • 1
  • 1
GhostCat
  • 137,827
  • 25
  • 176
  • 248