How to write Junit for Interface default methods

后端 未结 5 1954
梦如初夏
梦如初夏 2021-02-05 15:24

Please help in writing Junit for the interface default method.

public interface ABC {
    default List getSrc(DEF def, XYZ xyz) t         


        
相关标签:
5条回答
  • 2021-02-05 15:53

    I think there is a simpler way. It consists in implementing the interface with methods to be tested in the test class and invoking methods of default type directly. If necessary, the objects that are called internally are mocked. The previous example would be such that:

    Interface)

    public interface ABC<T, D, K, V> {
        default List<String> getSrc(DEF def, XYZ xyz) throws Exception {
          list<String>() result=new Arraylist<String>();
          result.add(def.toString());
          result.add(xyz.toString());
          return result;
        }
    }
    

    Test class)

    ...        
    @RunWith(MockitoJUnitRunner.class)
    public class ABCTest implements ABC{
    
        @Test
        public void testGetSrc() {
    
            list<String>() result=getSrc(new DEF("Hi!"),new XYZ("bye!"));
    
            int actual=result.size();
            int expected=2;
            assertEquals(expected, actual);
    
        }
    

    If you need to test the launch of an exception, it will be enough to force its release from wrong parameters and correctly configure the test:

    ...        
    @Test(expected = GenericRuntimeException.class)
    public void test....
    ...
    

    I've checked it with a similar code and it works. It is also collected correctly in the coverage analysis.

    0 讨论(0)
  • 2021-02-05 16:00

    As suggested in the answer, create implementation class for the interface and test it, for an example I modified getSrc method in your ABC interface, as below:

    import java.util.ArrayList;
    import java.util.List;
    
    public interface ABC<T, D, K, V> {
    
        default List<String> getSrc(DEF def, XYZ xyz) {
            final List<String> defaultList = new ArrayList<>();
            defaultList.add("default");
            defaultList.add("another-default");
            return defaultList;
        }
    }
    

    Created an implementation class for the same, optionally you can create another method calling super method and write @Test for both, as I does:

    import java.util.List;
    
    public class ABCImpl implements ABC<String, Integer, String, Integer> {
    
        public List<String> getSrcImpl(DEF def, XYZ xyz) {
            final List<String> list = getSrc(def, xyz);
            list.add("implementation");
            return list;
        }
    }
    

    Corresponding Test class for the implementation is as follows:

    import static org.hamcrest.MatcherAssert.assertThat;
    import static org.hamcrest.Matchers.empty;
    import static org.hamcrest.Matchers.is;
    import static org.hamcrest.Matchers.not;
    import static org.hamcrest.Matchers.contains;
    import java.util.Collection;
    import java.util.List;
    
    import org.junit.Before;
    import org.junit.Test;
    
    public class ABCImplTest {
    
        private ABCImpl abcImpl;
    
        @Before
        public void setup() {
            abcImpl = new ABCImpl();
        }
    
        @Test
        public void testGetSrc() throws Exception {
            List<String> result = abcImpl.getSrc(new DEF(), new XYZ());
            assertThat((Collection<String>) result, is(not(empty())));
            assertThat(result, contains("default", "another-default"));
        }
    
    
        @Test
        public void testABCImplGetSrc() throws Exception {
            List<String> result = abcImpl.getSrcImpl(new DEF(), new XYZ());
            assertThat((Collection<String>) result, is(not(empty())));
            assertThat(result, contains("default", "another-default", "implementation"));
        }
    }
    
    0 讨论(0)
  • 2021-02-05 16:12

    If you're using Mockito, the simplest way to unit-test a default (AKA "defender") method is to make a spy1 using the interface class literal2. The default method can then be invoked on the returned spy instance as normal. The following example demonstrates:

    import org.junit.Test;
    
    import static org.junit.Assert.assertFalse;
    import static org.junit.Assert.assertTrue;
    import static org.mockito.Mockito.spy;
    
    interface OddInterface {
        // does not need any unit tests because there is no default implementation
        boolean doSomethingOdd(int i);
    
        // this should have unit tests because it has a default implementation
        default boolean isOdd(int i) {
            return i % 2 == 1;
        }
    }
    
    public class OddInterfaceTest {
        OddInterface cut = spy(OddInterface.class);
    
        @Test
        public void two_IS_NOT_odd() {
            assertFalse(cut.isOdd(2));
        }
    
        @Test
        public void three_IS_odd() {
            assertTrue(cut.isOdd(3));
        }
    }
    

    (tested with Java 8 and mockito-2.24.5)

    1People often warn using a spy can be indicative of a code or test smell, but testing a default method is a perfect example of when using a spy is a good idea.

    2As of the time of this writing (2019), the signature of spy which accepts a class literal is marked as @Incubating, but has been around since mockito-1.10.12 which was released in 2014. Furthermore, support for default methods in Mockito has been around since mockito-2.1.0 which was released in 2016. It seems like a safe bet that this method will continue to work in future versions of Mockito.

    0 讨论(0)
  • 2021-02-05 16:12

    You can either create a class that implements your interface or make your test implement it. The second one seems to be a shorter solution:

    public class FunctionCallingTransactionTemplateTest implements FunctionCallingTransactionTemplate {
        private final Object object = Mockito.mock(Object.class);
    
        @Test
        public void should_invoke_function() throws Exception {
            // given
            when(object.toString()).thenReturn("salami");
    
            // when
            String result = functionCallingTransactionTemplate().execute((status) -> object.toString());
    
            // then
            assertThat(result).isEqualTo("salami");
        }
    }
    
    0 讨论(0)
  • 2021-02-05 16:13

    The answer is very straight forward. No mocking or spying needed for this just create an anonymous object for interface without overriding default methods.

    Ex:

    interface Adder {
      default sum(Integer...n) {
        return Arrays.stream(n).reduce(0, Integer::sum);
      }
    } 
    
    // Junit 4
    class AdderTest {
    
      private Adder adder;
    
      @Before
      public void setup() {}
        adder = new Adder(){}; // not overriding default methods
      }
    
      @Test
      public void testSum() {
        Assert.assertEquals(3, adder.sum(1, 2));
      }
    }
    
    0 讨论(0)
提交回复
热议问题