在 Spring 单元测试中连接依赖项而不使用 @RunWith

发布于 2025-01-04 04:46:22 字数 736 浏览 1 评论 0原文

我正在使用 spring 3 mvc/安全框架。

我创建了一个控制器类,它具有对存储库的引用以从中加载数据。该类用@Controller注释,存储库类用@Repository注释,存储库的实例是@Autowired

但是,当我尝试进行单元测试时,自动装配实例会引发空指针异常。

现在,我明白,因为它是自动装配的,所以它需要在 spring 上下文中才能被拾取。但我觉得如果我使用 @RunsWith() ,那么它就变成了集成测试。我真的很想将此方法的集成测试(使用@RunsWith)和单元测试分开。关于如何解决这个空指针异常有什么想法吗?在我的控制器类上创建 getter/setter 方法可以吗?

: 存储库类:

@Repository
public class Repository{
 ....
}

控制器类:

@Controller
public class Controller{
@Autowired
private Repository repo;
....
public String showView(){
    repo.doSomething();
}

测试类:

public ControllerTest {
@Test
public shouldDoTestOfShowView(){
}
}

I am using spring 3 mvc/security frameworks.

I have created a Controller class that has a reference to a repository to load data from. The class is annotated with @Controller, the repository class is annotated with @Repository, and the instance of the respository is @Autowired.

However when I try to unit test, the autowired instance throws a null pointer exception.

Now, I understand that because it is autowired, it needs to be within the spring context to be picked up. But I feel that if I use @RunsWith() then it becomes an integration test. I would really like to separate the integration tests (using @RunsWith) and the unit test for this method. Any ideas on how I can get around this null pointer exception? Would just creating getter/setter methods on my controller class be okay?:

Repository class:

@Repository
public class Repository{
 ....
}

Controller class:

@Controller
public class Controller{
@Autowired
private Repository repo;
....
public String showView(){
    repo.doSomething();
}

Test class:

public ControllerTest {
@Test
public shouldDoTestOfShowView(){
}
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

饮惑 2025-01-11 04:46:22

我个人倾向于使用 @Simon 的方法,而不是暴露 setter,尽管这两种方法都可以。不过,仅仅为了测试而添加设置器有点烦人。

另一种方法是使用 Spring 的 ReflectionTestUtils 使用反射直接将依赖项插入到字段中,无需特殊的构造函数和设置器,例如,

public ControllerTest {
   @Test
   public shouldDoTestOfShowView() {
      Controller controller = new Controller();
      Repository repository = new Repository();

      ReflectionTestUtils.setField(controller, "repo", repository); 
   }
}

无论是否仍然构成“集成测试”是你的决定(我不决定)。

I personally tend to use @Simon's approach, rather than exposing setters, although either approach is fine. Adding setters just for the sake of tests is a bit annoying, though.

An alternative is to use Spring's ReflectionTestUtils class to directly poke dependencies into the field using reflection, removing the need for special constructors and setters, e.g.

public ControllerTest {
   @Test
   public shouldDoTestOfShowView() {
      Controller controller = new Controller();
      Repository repository = new Repository();

      ReflectionTestUtils.setField(controller, "repo", repository); 
   }
}

Whether or not that still constitutes an "integration test" is your call (I don't).

栖迟 2025-01-11 04:46:22

我总是写 2 个构造函数。一种没有 Spring 参数,另一种受保护,具有单元测试的所有依赖项。

@Controller
public class Controller{
@Autowired
private Repository repo;

public Controller() {
    super();
}

protected Controller(Repositoy repo) {
    this();
    this.repo = repo;
}

I always write 2 constructors. One without arguments for Spring and a protected one with all the dependencies for unit testing.

@Controller
public class Controller{
@Autowired
private Repository repo;

public Controller() {
    super();
}

protected Controller(Repositoy repo) {
    this();
    this.repo = repo;
}
不疑不惑不回忆 2025-01-11 04:46:22

通常,我会提供 setter 只是为了测试目的,但如果您担心代码的使用者不正确地使用它们,也许您想要使用利用反射的方法。

我过去编写过一个实用程序,它接受两个参数,一个目标对象和一个您在目标上设置的对象。

public static void set(Object target, Object setMeOnTarget) {
    //
}

从这里你可以做的是内省 target 的字段,寻找 Spring 支持的自动装配注释(@Autowired@Resource@Inject,甚至可能是@Value)并查看是否可以将setMeOnTarget分配给该字段(我使用Class.isAssignableFrom(Class))。

它可能有点脆弱(Spring 突然停止支持这些注释......不太可能......),但它对我来说非常有用。

Normally, I would provide setters just for testing purposes, but if you are concerned about consumers of your code using them improperly, perhaps you want to use an approach that utilizes reflection.

I have written a utility in the past which takes in two arguments, a target object and an object you are setting on your target.

public static void set(Object target, Object setMeOnTarget) {
    //
}

What you can do from here is introspect on the fields of target, looking for Spring's supported autowiring annotations (@Autowired, @Resource, @Inject, maybe even @Value) and see if setMeOnTarget can be assigned to that field (I used Class.isAssignableFrom(Class)).

It may be a bit frail (Spring suddenly stops supporting these annotations...not likely...), but it has worked great for me.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文