Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to reuse method and test in JUnit?

I've tried to avoid duplicate code in JUnit test, but I'm kind of stuck.

This is my first test, for the second one it has exactly the same methods but different service (different input). instead of the TestCaseResourceTest1 I have TestCaseResourceTest2. Now what could be the proper way to test both? I want to have a separate file for test number 2, how should I avoid the duplicate code? (ex. use the beforeFileTest() method)

public class TestCaseResourceTest1 {

    @Mock
    private TestService testService;
    @Mock
    private AreaService areaService;

    private TestCaseService1 testCaseService1; // is changed in test2

    @Before
    public void before() throws Exception{
        testCaseService1 = mock(TestCaseService1.class); // is changed in test2
        MockitoAnnotations.initMocks(this);
        beforeFileTest();
    }

    private void beforeFileTest() throws Exception{
        doReturn(true).when(areaService).chechExists(any(String.class), eq(false));
    }

    @Test
    public void verifyFileExists() throws Exception{
        verifyOtherArea(testCaseService1); // is changed in test2
        doReturn(false).when(areaService).chechExists(any(String.class), eq(false));
    }
}

just lines with comment is changed in test2 are differences.

Tnx

like image 293
GeoCom Avatar asked Aug 07 '17 14:08

GeoCom


2 Answers

Given this excerpt from your question:

… instead of the TestCaseResourceTest1 I have TestCaseResourceTest2 … I want to have a separate file for test number 2

… the standard ways of sharing code between test cases are:

  • Create a Test Suite and include the shared code in the test suite (typically in @BeforeClass and @AfterClass methods). This allows you to (1) run setup code once (per suite invocation); (2) encapsulate shared setup/teardown code and (3) easily add more tests cases later. For example:

    @RunWith(Suite.class)
    @Suite.SuiteClasses({
        TestCaseResourceTest1.class,
        TestCaseResourceTest2.class
    )}
    public class TestSuiteClass {
    
        @BeforeClass
        public void setup() {
            beforeFileTest();
        }
    
        private void beforeFileTest() throws Exception {
            // ...
        }
    }
    
  • Create an abstract class which parents TestCaseResourceTest1 and TestCaseResourceTest2 and let those test cases call the shared code in the parent (typically via super() calls). With this approach you can declare default shared code in the parent while still allowing sub classes to (1) have their own behaviour and (2) selectively override the parent/default behaviour

  • Create a custom JUnit runner, define the shared behaviour in this runner and then annotate the relevant test cases with @RunWith(YourCustomRunner.class). More details on this approach here

Just to reiterate what some of the other posters have said; this is not a common first step so you may prefer to start simple and only move to suites or abstract classes or custom runners if your usage provides a compelling reason to do so.

like image 181
glytching Avatar answered Sep 19 '22 23:09

glytching


I had the such situation and it was a sign about wrong implementation design. We are talking about pure unit tests where we test exactly what is implemented in the production classes. If we need duplicated tests it means we probably have duplication in implementation.

How did I resolve it in my project?

  1. Extracted common logic into parent service class and implemented unit tests for it.
  2. For child services I implemented tests only for particular implemented code there. No more.
  3. Implemented an integration tests on real environment were both services were involved and tested completely.
like image 35
Dmytro Maslenko Avatar answered Sep 16 '22 23:09

Dmytro Maslenko