【问题标题】:Testing fields which are going to be initialized by field injection (CDI)将通过字段注入 (CDI) 初始化的测试字段
【发布时间】:2017-06-22 00:19:48
【问题描述】:

假设我们有以下 sn-p 作为完整 Java EE 应用程序的一部分:

@Singleton
public class LoginService {


  @Inject
  private UserDAO userDAO;



  protected boolean login(String username, String password){

   // [...]
   User user = userDAO.findByUsername(username);

  // [...]

  }


}

UserDAO 是一个接口,该接口有一个名为 DatabaseUserDAO 的特定类实现,它被注入到 userDAO 字段中。

现在我要为登录方法 e 编写一个测试。 G。 testLoginSuccessfulIfLoginDataCorrect()。但是因为我不想依赖数据库,所以我只想通过使用一个类来存根它,例如public class TestUserDAO implements UserDAO并注入这个而不是应该注入的默认类。实施它的可能性是什么?我们还假设没有构造函数注入或其他方式来初始化该字段。

【问题讨论】:

  • 我认为最面向 CDI 的方式是使用Alternative bean。如果启用,替代方案将取代原始 bean,并且任何注入都将使用此替代方案完成。你考虑过吗?我可以进一步详细说明,只是不确定这就是你所追求的。
  • 也可以看看CDI Unit。

标签: java jsf jakarta-ee dependency-injection cdi


【解决方案1】:

使用 arquillian (http://arquillian.org/) 和 mockito (http://site.mockito.org/) 或其派生词之一:

  • 创建要注入的 UserDAO 模型
  • 从您的测试类中创建一个捆绑包以及模拟
  • 创建一个 junit 测试,它将您的 LoginService 与注入的模拟一起获取并可以将其用于测试:

示例(根据实际代码修改以在某种程度上匹配您的类名): (注意:这种部署生成只适用于maven项目)

 @RunWith(Arquillian.class)
 public class LoginSeviceTest {
     // a pattern I find quite neat: hold the mocks in a static local class, but they might be anywhere else
     public static class LocalMocks {
         @Produces public static UserDAO mockUser = Mockito.mock(UserDAO.class);
     }

     @Deployment
     public static WebArchive createDeployment() {
         PomEquippedResolveStage pom = Maven.resolver().loadPomFromFile("pom.xml");
         BeansDescriptor beansXml = Descriptors.create(BeansDescriptor.class)
             .addDefaultNamespaces().getOrCreateAlternatives()
             .up();

         WebArchive jar = ShrinkWrap.create(WebArchive.class)
             .addAsLibraries(pom.resolve("org.mockito:mockito-core").withTransitivity().asFile())
             .addClass(LoginService.class)  // eventually further classes or packages you depend on
             .addClass(LoginSeviceTest.LocalMocks.class)
             .addAsWebInfResource(new StringAsset(beansXml.exportAsString()), "beans.xml");

         return jar;
     }

     @Inject LoginService loginService;

     @Test
     public void testLogin() {
         // use the injected loginService here for actual tests
     }
 }

请注意,您确实不需要以这种方式更改被测类以使测试成为可能。

【讨论】:

    【解决方案2】:

    嗯,这没有什么秘密。

    你有四种可能性。

    1. 为所有 DAO (EJB) 创建一个构造函数。

      @Singleton
      public class LoginService {
      
        @Inject
        private UserDAO userDAO;
      
        LoginService (UserDAO userDAO) {
           this.userDAO = userDAO;
        }
      
      }
      
    2. 为每个 DAO 创建一个集合。

      @Singleton
      public class LoginService {
      
        @Inject
        private UserDAO userDAO;
      
        setUserDAO(UserDAO userDAO) {
           this.userDAO = userDAO;
        }
      }
      
    3. 使用默认访问直接设置变量

      @Singleton
      public class LoginService {
      
        @Inject
        UserDAO userDAO;
      
      }
      

      在你的测试中:

      loginService.userDAO = userDAOMocked.
      

    因此,您可以模拟 UserDAO 并使用构造函数或 setter 作为参数传递给 LoginService 测试。

    另一个是by reflection(没有构造函数或setter),但我不喜欢这种方法...:

    1. 反思

      public static void setPrivateField(Class<? extends Object> instanceFieldClass, Object instance, String fieldName, Object fieldValue) throws Exception {
          Field setId = instanceFieldClass.getDeclaredField(fieldName);
          setId.setAccessible(true);
          setId.set(instance, fieldValue);
      }
      

      并使用:

      setPrivateField(loginService, "userDAO", userDAOMocked);
      

    【讨论】:

    • 在我看来,使用像 arquilian 这样的东西更现代。无需将所有这些代码添加到仅用于测试的类中
    • @Kukeltje,Arquillian 也是一个很好的解决方案,但会启动应用服务器(性能较低)并在项目中引入新的依赖项。并非上述所有解决方案都引入了新代码,其中一些解决方案使测试比使用 Arquillian 更简单(您的示例说明了这一点)。我认为我不应该对四个可行的解决方案投反对票,但没关系。
    • 1 2 和 3 都以不应该在实际应用程序中使用但可以完成的方式公开字段/构造函数。它是仅用于测试但可用于“生产”的代码(我已经看到它被(ab)使用)。而关于正在启动的“容器”,嗯,较少的性能也可以被视为更现实的性能。对于简单的案例 1 2 和 3 有效,但如果您不仅要进行单元测试,还要进行集成测试,事情就会变得更加复杂。注入这里,那里的东西......但你是对的,downvote不好......删除!干杯
    • 删除了反对票(但我确实认为其他答案应该有更多赞成票;-))
    猜你喜欢
    • 1970-01-01
    • 2010-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-16
    • 1970-01-01
    相关资源
    最近更新 更多