복붙노트

[SPRING] 테스트 중 @Autowired private 필드 삽입

SPRING

테스트 중 @Autowired private 필드 삽입

난 본질적으로 응용 프로그램에 대한 실행기입니다 구성 요소 설치 프로그램이 있습니다. 다음과 같이 구성됩니다.

@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 용 모의 객체를 만들어 테스트 클래스의 myLauncher에 삽입 할 수 있습니까? Spring이 autowiring을 처리 할 때 myLauncher에 getter 또는 setter가 없습니다. 가능한 경우 getter 및 setter를 추가하지 않아도됩니다. @Before init 메소드를 사용하여 모의 객체를 autowired 변수에 삽입하도록 테스트 케이스에 지시 할 수 있습니까?

만약 내가 완전히 잘못 될 경우 자유롭게 말할 수 있습니다. 나는 아직도 이것에 초보 다. 내 주요 목표는 setter 메소드를 작성하거나 applicationContext-test.xml 파일을 사용하지 않고도 @Autowired 변수에 mock 객체를 넣는 Java 코드 또는 주석을 그냥 사용하는 것이다. 필자는 테스트 용으로 별도의 애플리케이션 컨텐츠를 유지해야하는 대신 .java 파일의 테스트 케이스에 대한 모든 것을 유지하려고한다.

mockito 객체를 mockito로 사용하기를 희망합니다. 과거에는 org.mockito.Mockito를 사용하고 Mockito.mock (MyClass.class)을 사용하여 내 객체를 생성하여이 작업을 수행했습니다.

해결법

  1. ==============================

    1.MyLauncher에 모의 테스트를 할 수 있습니다. 나는 누군가를 사용하여 조롱하는 프레임 워크가 어떤 대답을 신속하게 제공하는지 보여 주면 확실합니다. mockito를 사용하여 @RunWith (MockitoJUnitRunner.class)를 사용하고 myLauncher에 주석을 사용하는 방법을 살펴 보았습니다. 그것은 아래 보이는 것과 같을 것입니다.

    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() {
    
        }
    }
    
  2. ==============================

    2.허용 된 대답 (MockitoJUnitRunner 및 @InjectMocks 사용)은 훌륭합니다. 그러나 좀 더 가벼운 (특별한 JUnit 러너가 아닌) 약간의 "마법"(더 투명)을 원한다면, 때때로 사적인 영역을 인트로 스펙 션을 사용하여 직접 설정할 수 있습니다.

    허용 된 대답 (MockitoJUnitRunner 및 @InjectMocks 사용)은 훌륭합니다. 그러나 좀 더 가벼운 (특별한 JUnit 러너가 아닌) 약간의 "마법"(더 투명)을 원한다면, 때때로 사적인 영역을 인트로 스펙 션을 사용하여 직접 설정할 수 있습니다.

    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);
            }
        }
    
  3. ==============================

    3.때로는 @Component를 리팩토링하여 생성자 또는 설정 기 기반 주입을 사용하여 테스트 케이스를 설정할 수 있습니다 (@Autowired는 여전히 신뢰할 수 있습니다). 이제 대신 테스트 스텁 (예 : Martin Fowler의 MailServiceStub)을 구현하여 조롱 프레임 워크없이 테스트를 완전히 만들 수 있습니다.

    때로는 @Component를 리팩토링하여 생성자 또는 설정 기 기반 주입을 사용하여 테스트 케이스를 설정할 수 있습니다 (@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 / test 디렉토리에있는 테스트와 src / main / java에 여전히 프로덕션 코드를 가질 수 있습니다.

    Mockito가 마음에 드시면 MockitoJUnitRunner에 감사드립니다. 그것은 @ 마누엘이 당신에게 보여준 것과 같은 "마술"을 할 수있게 해줍니다 :

    @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 러너를 사용하고 setUp () 메서드에서 MockitoAnnotations.initMocks ()를 호출하여 Mockito가 주석 된 값을 초기화하도록 할 수 있습니다. @InitMocks의 javadoc과 필자가 작성한 블로그 게시물에서 자세한 정보를 찾을 수 있습니다.

  4. ==============================

    4.이 링크를 봐

    이 링크를 봐

    그런 다음 테스트 케이스를 다음과 같이 작성하십시오.

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration({"/applicationContext.xml"})
    public class MyLauncherTest{
    
    @Resource
    private MyLauncher myLauncher ;
    
       @Test
       public void someTest() {
           //test code
       }
    }
    
  5. ==============================

    5.저는 봄에 새로운 사용자입니다. 나는 이것을위한 다른 해결책을 발견했다. 리플렉션을 사용하고 공개적으로 필요한 필드를 만들고 모의 객체를 할당합니다.

    저는 봄에 새로운 사용자입니다. 나는 이것을위한 다른 해결책을 발견했다. 리플렉션을 사용하고 공개적으로 필요한 필드를 만들고 모의 객체를 할당합니다.

    이것은 내 인증 컨트롤러이며 일부 Autowired 개인 속성이 있습니다.

    @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;
        }
    }
    

    그리고 이것은 AuthController에 대한 Junit 테스트입니다. 나는 대중에게 사유 재산을 필요로하고 그들에게 모의 물건을 할당하고 바위를 짓는다.

    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);
            }
        }
    
    }
    

    이 방법으로 장점과 단점을 알지 못하지만이 방법이 효과적입니다. 이 기술은 조금 더 많은 코드를 가지고 있지만 이러한 코드는 다른 방법 등으로 분리 될 수 있습니다.이 질문에 대해서는 더 좋은 대답이 있지만 다른 해결책을 지적하고자합니다. 내 하찮은 영어 실력에 죄송하다는 말씀을 드리고 싶습니다. 모두에게 좋은 자바있으세요 :)

  6. ==============================

    6.myLauncher 클래스 (myService 용)에서 자동 배선 작업을 수행하려면 myLauncher를 자동 배선하여 생성자를 호출하는 대신 Spring에서 초기화하도록해야합니다. 자동 배선이되면 (myService 또한 자동 배선 됨) Spring (1.4.0 이상)은 테스트에 사용할 수있는 @MockBean 주석을 제공합니다. 이것은 컨텍스트 내 일치하는 단일 빈을 해당 유형의 모의로 대체합니다. 그런 다음 @Before 메서드에서 원하는 조롱 정의를 추가로 정의 할 수 있습니다.

    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
    }
    

    언급 된 다른 방법에 비해 이것의 몇 가지 장점은 다음과 같습니다.

  7. from https://stackoverflow.com/questions/16426323/injecting-autowired-private-field-during-testing by cc-by-sa and MIT license