我有一个组件的配置,它基本上是一个应用程序的启动。 它被配置,如下所示:
@Component
public class MyLauncher {
@Autowired
MyService myService;
//other methods
}
为MyService被标注与@Service
春注解,自动连接到我的启动类没有任何问题。
我想写一些JUnit测试用例MyLauncher,这样做,我开始像这样的类:
public class MyLauncherTest
private MyLauncher myLauncher = new MyLauncher();
@Test
public void someTest() {
}
}
我可以创建为MyService一个模仿对象,并将其注入到myLauncher在我的测试类? 我目前没有在myLauncher getter或setter的春天正在处理的自动装配。 如果可能的话,我想没有添加getter和setter方法。 我可以告诉测试用例注入模拟对象为使用的自动装配Autowired变量@Before
init方法?
如果我要对此完全错误的,随意的说。 我还是新来此。 我的主要目标是刚刚有把一个mock对象在某些Java代码或注释@Autowired
变量没有我不得不写一个setter方法或不必使用applicationContext-test.xml
文件。 我宁愿保持一切为了测试案例中.java
文件,而不必维护一个单独的应用程序内容只是我的测试。
我希望能使用为的Mockito的模仿对象。 在过去,我已经通过这样做org.mockito.Mockito
和创建我的对象Mockito.mock(MyClass.class)
Answer 1:
您可以绝对注入上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() {
}
}
Answer 2:
接受答案(使用MockitoJUnitRunner
和@InjectMocks
)是伟大的。 但是,如果你想要的东西多一点轻量级(没有特殊的JUnit运行),和更少的“神奇”(更加透明),尤其是偶尔使用,你可以只使用直接内省设置私有字段。
如果你使用Spring,你已经有了这个工具类: org.springframework.test.util.ReflectionTestUtils
使用非常简单:
ReflectionTestUtils.setField(myLauncher, "myService", myService);
第一个参数是你的目标bean,二是(通常是私有)字段的名称,最后是注入价值。
如果不使用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);
}
}
Answer 3:
有时候,你可以重构你的@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()在setUp()
方法让初始化的Mockito注释值。 你可以找到的javadoc的更多信息@InitMocks ,并在博客中 ,我已经写了。
Answer 4:
看看这个链接
然后再编写测试用例作为
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"/applicationContext.xml"})
public class MyLauncherTest{
@Resource
private MyLauncher myLauncher ;
@Test
public void someTest() {
//test code
}
}
Answer 5:
我相信,以对您的MyLauncher类自动装配工作(用于为myService),你需要让Spring初始化它,而不是调用构造函数,通过自动布线myLauncher。 一旦被自动有线(和为myService也越来越自动有线),春季(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或规则。
Answer 6:
我是春天的新用户。 我发现这是一个不同的解决方案。 使用反射,使公众必要的字段,并指定模拟对象。
这是我的身份验证控制器和它有一些自动装配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);
}
}
}
我不知道的优点和缺点的这种方式,但这是工作。 这TECHNIC有一点点的代码,但这些代码可以通过不同的方法等,有对这个问题更良好的答案被分隔,但我想指向不同的解决方案。 对不起,我的英语不好。 有一个好的java大家:)
文章来源: Injecting @Autowired private field during testing