테스트 중 @Autowired 비공개 필드 삽입
본질적으로 응용 프로그램의 시작 프로그램 인 구성 요소 설정이 있습니다. 다음과 같이 구성됩니다.
@Component
public class MyLauncher {
@Autowired
MyService myService;
//other methods
}
MyService는 @Service
Spring 주석으로 주석 처리되고 문제없이 내 런처 클래스에 자동 연결됩니다.
MyLauncher에 대한 jUnit 테스트 케이스를 작성하고 싶습니다. 이렇게하려면 다음과 같은 클래스를 시작했습니다.
public class MyLauncherTest
private MyLauncher myLauncher = new MyLauncher();
@Test
public void someTest() {
}
}
MyService에 대한 Mock 객체를 생성하여 테스트 클래스의 myLauncher에 삽입 할 수 있습니까? Spring이 자동 연결을 처리하기 때문에 현재 myLauncher에 getter 또는 setter가 없습니다. 가능하다면 게터와 세터를 추가 할 필요가 없습니다. @Before
init 메서드를 사용하여 자동 연결 변수에 모의 객체를 주입하도록 테스트 케이스에 지시 할 수 있습니까 ?
내가 이것에 대해 완전히 틀렸다면 자유롭게 말하십시오. 나는 아직 이것에 익숙하지 않습니다. 내 주요 목표는 @Autowired
setter 메소드를 작성하거나 applicationContext-test.xml
파일 을 사용하지 않고도 해당 변수에 모의 객체를 넣는 Java 코드 또는 주석을 갖는 것 입니다. 테스트 .java
를 위해 별도의 애플리케이션 콘텐츠를 유지하는 대신 파일 의 테스트 케이스에 대한 모든 것을 유지하는 것이 훨씬 낫습니다.
모의 객체에 Mockito 를 사용하고 싶습니다 . 과거 org.mockito.Mockito
에는 Mockito.mock(MyClass.class)
.
테스트에서 MyLauncher에 모의를 절대적으로 주입 할 수 있습니다. 누군가를 사용하고있는 조롱 프레임 워크를 보여 주면 답변을 빨리 제공 할 수있을 것입니다. mockito를 사용하면 @RunWith (MockitoJUnitRunner.class)를 사용하고 myLauncher에 대한 주석을 사용할 수 있습니다. 아래와 같이 보일 것입니다.
@RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest
@InjectMocks
private MyLauncher myLauncher = new MyLauncher();
@Mock
private MyService myService;
@Test
public void someTest() {
}
}
허용되는 대답 ( MockitoJUnitRunner
및 사용 @InjectMocks
)은 훌륭합니다. 그러나 좀 더 가볍고 (특별한 JUnit 실행기가 없음) 특히 가끔 사용하기 위해 덜 "마 법적"(더 투명 함)을 원한다면 introspection을 사용하여 직접 private 필드를 설정할 수 있습니다.
Spring을 사용하는 경우 이미 이에 대한 유틸리티 클래스가 있습니다. org.springframework.test.util.ReflectionTestUtils
사용은 매우 간단합니다.
ReflectionTestUtils.setField(myLauncher, "myService", myService);
첫 번째 인수는 대상 빈이고 두 번째 인수는 (일반적으로 개인) 필드의 이름이며 마지막 인수는 삽입 할 값입니다.
Spring을 사용하지 않는 경우 이러한 유틸리티 메서드를 구현하는 것은 매우 간단합니다. 이 Spring 클래스를 찾기 전에 사용한 코드는 다음과 같습니다.
public static void setPrivateField(Object target, String fieldName, Object value){
try{
Field privateField = target.getClass().getDeclaredField(fieldName);
privateField.setAccessible(true);
privateField.set(target, value);
}catch(Exception e){
throw new RuntimeException(e);
}
}
때로는 @Component
생성자 또는 setter 기반 주입을 사용하여 테스트 케이스를 설정하도록 리팩터링 할 수 있습니다 (여전히를 사용할 수 있음 @Autowired
). 이제 테스트 스텁 (예 : Martin Fowler의 MailServiceStub )을 대신 구현하여 모의 프레임 워크없이 테스트를 완전히 만들 수 있습니다 .
@Component
public class MyLauncher {
private MyService myService;
@Autowired
MyLauncher(MyService myService) {
this.myService = myService;
}
// other methods
}
public class MyServiceStub implements MyService {
// ...
}
public class MyLauncherTest
private MyLauncher myLauncher;
private MyServiceStub myServiceStub;
@Before
public void setUp() {
myServiceStub = new MyServiceStub();
myLauncher = new MyLauncher(myServiceStub);
}
@Test
public void someTest() {
}
}
이 기술은 테스트중인 클래스와 테스트중인 클래스가 동일한 패키지에있는 경우 특히 유용합니다. 기본 패키지 전용 액세스 수정자를 사용하여 다른 클래스가 액세스하지 못하도록 할 수 있기 때문입니다. 프로덕션 코드는 여전히있을 수 src/main/java
있지만 테스트는 src/main/test
디렉터리에있을 수 있습니다.
Mockito가 마음에 들면 MockitoJUnitRunner 를 고맙게 생각할 것 입니다. @Manuel이 보여준 것과 같은 "마법"을 할 수 있습니다.
@RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest
@InjectMocks
private MyLauncher myLauncher; // no need to call the constructor
@Mock
private MyService myService;
@Test
public void someTest() {
}
}
또는 기본 JUnit 실행기를 사용하고 메소드에서 MockitoAnnotations.initMocks () 를 호출하여 MockitosetUp()
가 주석이 달린 값을 초기화하도록 할 수 있습니다. @InitMocks 의 javadoc 과 내가 작성한 블로그 게시물 에서 자세한 정보를 찾을 수 있습니다 .
이 링크를 봐
그런 다음 테스트 케이스를 다음과 같이 작성하십시오.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"/applicationContext.xml"})
public class MyLauncherTest{
@Resource
private MyLauncher myLauncher ;
@Test
public void someTest() {
//test code
}
}
MyLauncher 클래스 (myService 용)에서 자동 연결 작업을 수행하려면 myLauncher를 자동 연결하여 생성자를 호출하는 대신 Spring이 초기화하도록해야합니다. 자동 연결되면 (그리고 myService도 자동 연결됨) Spring (1.4.0 이상)은 테스트에 넣을 수있는 @MockBean 주석을 제공합니다. 이것은 컨텍스트에서 일치하는 단일 빈을 해당 유형의 모의로 대체합니다. 그런 다음 @Before 메서드에서 원하는 조롱을 추가로 정의 할 수 있습니다.
public class MyLauncherTest
@MockBean
private MyService myService;
@Autowired
private MyLauncher myLauncher;
@Before
private void setupMockBean() {
doNothing().when(myService).someVoidMethod();
doReturn("Some Value").when(myService).someStringMethod();
}
@Test
public void someTest() {
myLauncher.doSomething();
}
}
MyLauncher 클래스는 수정되지 않은 상태로 유지 될 수 있으며 MyService 빈은 정의한대로 메소드가 값을 반환하는 모의 객체가됩니다.
@Component
public class MyLauncher {
@Autowired
MyService myService;
public void doSomething() {
myService.someVoidMethod();
myService.someMethodThatCallsSomeStringMethod();
}
//other methods
}
언급 된 다른 방법에 비해 이것의 몇 가지 장점은 다음과 같습니다.
- myService를 수동으로 삽입 할 필요가 없습니다.
- Mockito 러너 또는 규칙을 사용할 필요가 없습니다.
I'm a new user for Spring. I found a different solution for this. Using reflection and making public necessary fields and assign mock objects.
This is my auth controller and it has some Autowired private properties.
@RestController
public class AuthController {
@Autowired
private UsersDAOInterface usersDao;
@Autowired
private TokensDAOInterface tokensDao;
@RequestMapping(path = "/auth/getToken", method = RequestMethod.POST)
public @ResponseBody Object getToken(@RequestParam String username,
@RequestParam String password) {
User user = usersDao.getLoginUser(username, password);
if (user == null)
return new ErrorResult("Kullanıcıadı veya şifre hatalı");
Token token = new Token();
token.setTokenId("aergaerg");
token.setUserId(1);
token.setInsertDatetime(new Date());
return token;
}
}
And this is my Junit test for AuthController. I'm making public needed private properties and assign mock objects to them and rock :)
public class AuthControllerTest {
@Test
public void getToken() {
try {
UsersDAO mockUsersDao = mock(UsersDAO.class);
TokensDAO mockTokensDao = mock(TokensDAO.class);
User dummyUser = new User();
dummyUser.setId(10);
dummyUser.setUsername("nixarsoft");
dummyUser.setTopId(0);
when(mockUsersDao.getLoginUser(Matchers.anyString(), Matchers.anyString())) //
.thenReturn(dummyUser);
AuthController ctrl = new AuthController();
Field usersDaoField = ctrl.getClass().getDeclaredField("usersDao");
usersDaoField.setAccessible(true);
usersDaoField.set(ctrl, mockUsersDao);
Field tokensDaoField = ctrl.getClass().getDeclaredField("tokensDao");
tokensDaoField.setAccessible(true);
tokensDaoField.set(ctrl, mockTokensDao);
Token t = (Token) ctrl.getToken("test", "aergaeg");
Assert.assertNotNull(t);
} catch (Exception ex) {
System.out.println(ex);
}
}
}
I don't know advantages and disadvantages for this way but this is working. This technic has a little bit more code but these codes can be seperated by different methods etc. There are more good answers for this question but I want to point to different solution. Sorry for my bad english. Have a good java to everybody :)
참고URL : https://stackoverflow.com/questions/16426323/injecting-autowired-private-field-during-testing
'Programing' 카테고리의 다른 글
C ++에서 부호없는 double? (0) | 2020.11.25 |
---|---|
Azure SQL Database 웹 대 비즈니스 에디션 (0) | 2020.11.25 |
Angular 지시문 템플릿에 조건부로 데이터 속성 추가 (0) | 2020.11.24 |
"이 비동기 메서드에는 'await'연산자가 없으며 동 기적으로 실행됩니다."경고에 대해 걱정해야합니까? (0) | 2020.11.24 |
경고 : 주소 공간 무작위 화 비활성화 오류 : 작업이 허용되지 않습니다. (0) | 2020.11.24 |