0

I have a class like below:

public class Connection{

    public boolean isDBConnectionRelativeException(String key) {
        Set<String> keySet = new HashSet<>();
        keySet.add("key1");
        keySet.add("key2");
        keySet.add("key3");
        if (keySet.contains(key)) {
            return true;
        }
        return false;
    }
}

and I don't know how to do unit test with it

I find similar situation in [How to mock the return value of a Map?

but I think it is not a same problem. I try to do it with PowerMockito as below, but it doesn't work

@RunWith(PowerMockRunner.class)
@PrepareForTest({HashSet.class})
public class ConnectionExceptionAspectTest {
    private Connection connectionExceptionAspect;
    @Before
    public void init(){
        Mockito.validateMockitoUsage();
        connectionExceptionAspect = new Connection();
    }
    @Test
    public void isDBConnectionRelativeExceptionMock() {

        Set<String> stringSet = new HashSet<>();
        Set<String> clazzSet = spy(stringSet);
        try {
            PowerMockito.whenNew(HashSet.class).withNoArguments().thenReturn((HashSet) clazzSet);
            PowerMockito.when(clazzSet.contains("key")).thenReturn(true);

        }catch (Exception e){
            e.printStackTrace();
        }
       assertTrue(connectionExceptionAspect.isDBConnectionRelativeException("key"));

    }
}

please help me make a unit test with it!thanks!

**//after edit one time **
In the real situation, Set keySet is constructed with

Set<String> keySet = new HashSet<>();

,keySet.add(element) can't run in test case, it is more complicated. so I just want to mock it to get my result like

PowerMockito.when(keySet.contains("key")).thenReturn(true);

I know PowerMockito can mock a local new variable instance, but I don't know how to do with a local new map instance. I can't find any about this. I am very grateful to all those who are concerned about this issue. My English is terrible to explain my question.

//after edit second time
I know how to mock a POJO local new variable instance, but I don't know how to mock a Set< String >, Map< String,String > etc local new variable instance. I think they are quite different. that's the problem: how to mock a local Set< String >, Map< String,String > etc instance.

  • The test does not appear to match the method under test used in the example. – Nkosi Sep 03 '19 at 08:39
  • Without a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) that highlight exactly what was done, it’s hard to reproduce the problem, allowing a better understanding of what is being asked. – Nkosi Sep 03 '19 at 08:40
  • you can't mock a local variable, neither do you have to. – Stultuske Sep 03 '19 at 08:42
  • @Nkosi oh.. my fault – chenshuichuan Sep 03 '19 at 08:48
  • @Stultuske no, powerMock can mock a local variable – chenshuichuan Sep 03 '19 at 08:50
  • 3
    This test seems to be useless. You'd end up testing the `Set.contains()` method, and since you want to use mock values, it would make the test even more useless (if possible). – Kayaman Sep 03 '19 at 09:19
  • What are you trying to test in fact? What are "key1"? I imagine they are dynamic and you don't know how many you will have? – wi2ard Sep 03 '19 at 10:02
  • @GabiM yes, the keys in the Set is Uncertain, and keySet.add(element) can't be run in test case. so I just want to mock a local Set to ignore its .add(element) operation and test next step like *keySet.contains(key)* and assume this can be true – chenshuichuan Sep 03 '19 at 10:15

4 Answers4

0

There is no need to mock local variable for the above method.

@Test
    public void isDBConnectionRelativeExceptionMock() {
       assertTrue(connection.isDBConnectionRelativeException("key1"));
       assertTrue(connection.isDBConnectionRelativeException("key4"));
    }
Sujay Mohan
  • 933
  • 7
  • 14
  • thank you for your answer. I understand what you mean. but in the real situation , Set keySet is not constructed like my example, it is more complicated and can't construct in test case. so I just want to mock it. – chenshuichuan Sep 03 '19 at 08:55
  • 2
    @chenshuichuan either it is constructed in such a way, or it might be by a service call which can easily be mocked. so far, all we can do is guess – Stultuske Sep 03 '19 at 09:03
  • you can refer this example on the use of power mock for local variables. https://stackoverflow.com/questions/20014547/how-to-mock-local-variables-using-mockito-or-powermock – Sujay Mohan Sep 03 '19 at 09:34
  • @Sujay Mohan thank you. I know how to mock a POJO local new variable instance, but I don't know how to mock a set, Map etc local new variable instance. I think they are quite different. that's the problem: how to mock a local set, Map etc instance. – chenshuichuan Sep 03 '19 at 09:46
0

I'm not sure if that will be answer to your question, but try this code:

@Test
public void isDBConnectionRelativeExceptionMock() {
    Set<String> stringSet = mock(HashSet.class);
    when(stringSet.add(anyString())).then(invocation -> false);
    when(stringSet.contains("java.net.ConnectException")).thenReturn(true);
    try {
        PowerMockito.whenNew(HashSet.class).withNoArguments().thenReturn((HashSet) stringSet);
    }catch (Exception e){
        e.printStackTrace();
    }
    assertTrue(dataRepository.isDBConnectionRelativeException("java.net.ConnectException"));
}
Mershel
  • 542
  • 1
  • 9
  • 17
0

You will need to change your PrepareForTest annotation to @PrepareForTest({Connection.class}).

From the PowerMockito docu:

This annotation tells PowerMock to prepare certain classes for testing. Classes needed to be defined using this annotation are typically those that needs to be byte-code manipulated


As others pointend out doing things like that isn't the best way do solve this problem. If you want a more "usefull" answer provide a real example of what your code is actually doing.

Ideally the HashSet should be contained or delivered by another dependency that could be mocked.


I know how to mock a POJO local new variable instance, but I don't know how to mock a Set, Map etc local new variable instance. I think they are quite different. that's the problem: how to mock a local Set< String>, Map etc instance.

There is no mentionable difference. You mock the class and define the behaviour. But the usual suggestion is not to mock objects you can easily create.

second
  • 4,069
  • 2
  • 9
  • 24
0

Why don't you just mock Connection and have isDBConnectionRelativeExceptionMock always return true?

Alternatively, you can have a new method buildKeySet that returns your Set and then have a spy to keep some of the functionality in place and some mocked. So you wouldn't need to mock a variable but a method - straight forward

As a rule of thumb, whenever you have difficulty unit testing some code it is a sign the code you are testing is not well enough designed. One big benefit of unit testing is that it points out the need to refactor. To be honest, spies are also a bit of a code smell but not undestanding what your code does or even what you are trying to test I can't suggest better.

Also, PowerMock is kind of a cannon you shouldn't use to shoot small flies, think twice before using it: do you really need to? is the code you're testing in need of some refactoring?

wi2ard
  • 1,471
  • 13
  • 24
  • thank you, My task is to test Connection class, and get test Coverage as more as possible,so my test case should run into isDBConnectionRelativeExceptionMock. And I can't change the source code, I have no rights to change source code. T_T – chenshuichuan Sep 04 '19 at 08:17
  • Like @kayaman also commented I don't see the value in this test, basically you are trying to mock a method so that it returns always true and then you assert that it returns true. You could test instead that the connection attributes (I assume) that you have in keySet contain the values you expect. It should be easier to implement and more useful. – wi2ard Sep 04 '19 at 10:09