测试期间,喷射@Autowired私人领域(Injecting @Autowired private

2019-09-02 04:35发布

我有一个组件的配置,它基本上是一个应用程序的启动。 它被配置,如下所示:

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

该上提到的其他方法的几个优点是:

  1. 你并不需要手动注入为myService。
  2. 你不需要使用亚军的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