【问题标题】:How to Mock an @Inject Interface如何模拟@Inject 接口
【发布时间】:2016-02-10 20:36:35
【问题描述】:

我有这个服务接口

public interface ABC {
    public String getAbc();
}

我有一个使用这个服务的控制器

public class Do {
    @Inject ABC abc;

    public String doAbc() {
       String a = abc.getAbc();
    }
}

在我的单元测试中,我希望能够模拟出这个 ABC 而不必提供实际的实现。以下是有效的:

public class TestDo {
   @Mock
   private Do do;

   @Mock
   private ABC abc;

   @Before
   public void init() {
       MockitoAnnotations.initMocks(this);
       do.abc = abc;
   }

   @Test
   public void testDo() {
       when(do.doAbc()).thenCallRealMethod();
       do.doAbc();
       assertSomething...
   }
}

但是我不想做的是do.abc = abc;,从我所有的搜索来看,为了做@InjectMock,我需要提供一个具体的类。有没有办法让我使用该接口,但又能够将模拟接口注入 do 而无需在 Do Class 中暴露我的 ABC 注入?我想让 ABC 成为私有变量,并且仍然能够通过 Mockito 注入接口。

【问题讨论】:

    标签: java unit-testing junit mockito


    【解决方案1】:
    public interface ABC {
        public String getAbc();
    }
    
    public class Do {
    
        @Inject
        ABC abc;
    
        public String doAbc() {
            System.out.println("Injected abc is " + (abc == null ? "null" : "not null"));
            return abc.getAbc();
        }
    
    }
    
    
    public class TestDo {
        @Mock
        private ABC abc;
    
        @InjectMocks
        private Do ddo;
    
        @Before
        public void init() {
            MockitoAnnotations.initMocks(this);
        }
    
        @Test
        public void simpleTest() {
            System.out.println(ddo.doAbc());
        }
    
    }
    

    输出是:

    Injected abc is not null
    null
    

    【讨论】:

    • @Churk,使用 Mockito 1.9.5、JDK 1.8 对其进行了测试。所有作品。我会将我的答案更新为完整的工作代码。
    • 我重新阅读了您的代码。我的错误是我在我的实际代码中使用了 /@InjectMocks 相当于 ABC,因此,它试图创建一个让我失望的接口实例。并且有 /@Mock 相当于 Do,所以我的 mocking 和 injectMocking 是落后的。
    【解决方案2】:

    您给出的示例是构造函数注入的完美案例:您可以将依赖项设为私有(甚至是最终的,这在实际情况下总是更可取)并且仍然构建实例而不需要注入框架:

    public class Do {
        private final ABC abc;
    
        @Inject
        public Do(final ABC abc) {
            this.abc = abc;
        }
    
        public String doAbc() {
           String a = abc.getAbc();
        }
    }
    

    【讨论】:

    • 我不想把 ABC 全部曝光。我想做@Inject private ABC abc;
    • @Churk 你的ABC 是一个依赖项。你的Do必须拥有它。使用字段注入来隐藏依赖关系只会使某些东西更有可能在以后崩溃——除了使测试更加困难之外。 DI 的全部意义在于通过使它们显式来消除魔术依赖。
    • 不是隐藏依赖,而是在我的实际使用中,它是一个DataAccessService,我不希望任何人能够直接访问这个Service。这就是为什么我希望它是私有的。
    • @Churk 听起来您不了解 Java 访问修饰符的用途。 DI 系统 必须 访问服务以无论如何注入它,因此您不会通过使用构造函数注入来暴露任何额外内容。
    • 如果我错了,请纠正我,你的构造函数允许这个Do do = new Do(myABC);,现在从安全的角度来看,我只是告诉我的应用程序允许构建这个服务的人注入他们自己的 ABC 类。与@Inject private ABC abc 相比,我的构造函数不接受任何强制注入仅在弹簧级别完成的 ABC 类。那么我了解java访问还是需要解释更多?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多