Модульное тестирование с Mockito - PullRequest
6 голосов
/ 28 декабря 2011

Я пишу модульные тесты для сервисного уровня в моем приложении Spring.
Вот мой класс обслуживания

    @Service
    public class StubRequestService implements RequestService {    
        @Autowired
        private RequestDao requestDao;  

        @Transactional(propagation = Propagation.REQUIRED, readOnly = true)
        @Override
        public Request getRequest(Long RequestId) {
            Request dataRequest = requestDao.find(requestId);
            return dataRequest;
        }
    }  

Вот мой класс тестирования

@RunWith(MockitoJUnitRunner.class)
@ContextConfiguration(locations = { "/META-INF/spring/applicationContext.xml" })
public class StubRequestServiceTest {

    @Mock
    public RequestDao requestDao;

    StubRequestService stubRequestService;  // How can we Autowire this ?

    @org.junit.Before
    public void init() {
      stubRequestService = new StubRequestService();  // to avoid this 
      stubRequestService.setRequestDao(dataRequestDao);  
      // Is it necessary to explicitly set all autowired elements ?  
      // If I comment/remove above setter then I get nullPointerException 
    }

    @Test
    public void testGetRequest()  {
        Request request = new Request();
        request.setPatientCnt("3");
        when(requestDao.find(anyLong())).thenReturn(request);
        assertEquals(stubRequestService.getRequest(1234L).getPatientCnt(),3);
    }    
}   

Работает нормальноно у меня есть несколько вопросов

  1. Как мы можем Autowire класс обслуживания в тесте?Я использую конструктор в init() метод для создания объекта службы.
  2. Нужно ли устанавливать все элементы Autowire для класса обслуживания?Для ex StubRequestService есть автосвязь RequestDao, которую мне нужно установить явно перед вызовом метода test, иначе он дает nullPointerException, так как requestDao равен null в StubRequestService.getRequest методе.
  3. Каким рекомендациям следует придерживаться при модульном тестировании сервисного уровня Spring?(Если я что-то не так делаю).

Ответы [ 3 ]

7 голосов
/ 28 декабря 2011

Ваш тест в порядке.Он даже не должен иметь аннотацию @ContextConfiguration.

Весь смысл инфраструктуры внедрения зависимостей, такой как Spring, состоит в том, чтобы иметь возможность объединять сервисы тестирования, просто создавая их экземпляры, устанавливая фиктивные зависимости, а затем вызывая их методы.

Вы делаете это правильно.Вам не нужно иметь Spring-контекст для таких модульных тестов.Вот почему их называют модульными тестами: они тестируют его изолированно от всех своих реальных зависимостей, включая Spring.

Примечание: если вы используете JUnit, аргументы метода assertXxx должны быть заменены.Ожидаемое значение предшествует фактическому значению.Это становится важным, когда утверждение не выполняется, и у вас появляется сообщение типа «ожидал 6, но было 3», а не «ожидал 3, но было 6».

3 голосов
/ 04 января 2012

Или Вы можете использовать springockito https://bitbucket.org/kubek2k/springockito/wiki/Home, сделает ваши тесты чище

3 голосов
/ 28 декабря 2011
  1. Если вы действительно чувствуете, что это облегчит ваши тесты - вы можете инициализировать весенний контекст и извлечь все объекты оттуда. Однако обычно для этого требуется создание отдельного XML-файла конфигурации пружины специально для тестов, поэтому я не рекомендовал бы его.

    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("testApplicationContext.xml");
    stubRequestService = (RequestService)applicationContext.getBean("myRequestServiceBean");
    
  2. (и 3) По сути, я предпочитаю тестировать каждый компонент моего приложения в полной изоляции друг от друга, и поэтому я не рекомендую то, что я описал в [1].

Это означает, что вы берете отдельный логический фрагмент своего приложения и тестируете только его, а полностью копирует все, к чему оно пытается получить доступ.

Допустим, у вас есть три класса:

//Fetches stuff from some webservice and converts to your app domain POJOs
class DataAccessLayer {
    public void setWebservice(Webservice ws) {...};

    public MyObject getMyObject() {...};
}

//Formats the domain POJOs and sends them to some kind of outputstream or stuff.
class ViewLayer {
    public void setOutputStream(OutputStream os) {...};

    public void viewMyObject(MyObject mo) {...};
}

//Main entry point of our MyObject fetch-process-display workflow
class Controller {
    public void setDataAccessLayer(DataAccessLayer dal) {...};
    public void setViewLayer(ViewLayer vl) {...};

    public void showMyObject() {
        MyObject mo = dal.getMyObject();
        ...some processing here maybe...
        vl.viewMyObject(mo);
    }
}

Теперь, какие тесты мы можем написать здесь?

  1. Проверьте, правильно ли DataAccessLayer преобразует объект из макета WS в объект нашего домена.
  2. Проверьте, правильно ли ViewLayer форматирует данный объект и записывает его в макетированный выходной поток.
  3. Проверьте, принимает ли Controller объект из макета DataAccessLayer обрабатывает его должным образом и отправляет макета ViewLayer.
...