Java 将模拟对象注入实际实现所在的位置

Java 将模拟对象注入实际实现所在的位置,java,junit,mocking,easymock,inject,Java,Junit,Mocking,Easymock,Inject,我想知道这个代码是什么意思: mathApplication.setCalculatorService(calcService); 为什么我要使用接口并从中生成对象?注射意味着什么 这是我的测试仪代码: import org.easymock.EasyMock; import org.easymock.EasyMockRunner; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import

我想知道这个代码是什么意思:

mathApplication.setCalculatorService(calcService);
为什么我要使用接口并从中生成对象?注射意味着什么

这是我的测试仪代码:

import org.easymock.EasyMock;
import org.easymock.EasyMockRunner;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(EasyMockRunner.class)
public class MathApplicationTester {

    private MathApplication mathApplication;
    private CalculatorService calcService;

    @Before
    public void setUp() {
        mathApplication = new MathApplication();
        calcService = EasyMock.createMock(CalculatorService.class);
        mathApplication.setCalculatorService(calcService);
    }

    @Test
    public void testAddAndSubtract() {

        //add the behavior to add numbers
        EasyMock.expect(calcService.add(20.0, 10.0)).andReturn(30.0);

        //subtract the behavior to subtract numbers
        EasyMock.expect(calcService.subtract(20.0, 10.0)).andReturn(10.0);

        //activate the mock
        EasyMock.replay(calcService);

        //test the subtract functionality
        Assert.assertEquals(mathApplication.subtract(20.0, 10.0), 10.0, 0);

        //test the add functionality
        Assert.assertEquals(mathApplication.add(20.0, 10.0), 30.0, 0);

        //verify call to calcService is made or not
        EasyMock.verify(calcService);
    }
}

MathApplication
依赖于
CalculatorService
,正如其名称所示,该服务提供计算服务

但是,在单元测试中,您只想测试被测试的类(
MathApplication
),因此您想用自己的实现替换所有依赖项,您可以完全控制这些实现。为此,您可以使用mock

calcService = EasyMock.createMock(CalculatorService.class);
依赖项注入是一种模式,它将依赖的对象“注入”到主对象中

有三种方法可以获取类所依赖的对象的实例

public class MathApplication {
    // I need an instance of CalculatorService inside the code of MathApplication
    ...
}
1:实例化
MathApplication
代码中的对象(无论
calculatorService
是本地属性还是类属性)。这不是很值得推荐的方式

public double subtract(double a, double b) {
    CalculatorService calculatorService = new SomeFastCalculatorService();
    return calculatorService.subtract(a, b);
}
2:将实例化任务委托给外部提供程序,称为工厂:

3:让外部人员通过提供注入点注入实例,或者通过构造函数,或者通过setter方法

它通常用于构建整个应用程序的“依赖关系树”(通常使用诸如或之类的框架)。在这里,它用于将模拟对象注入到被测试的类中:

mathApplication.setCalculatorService(calcService);
稍后,在
@Test
方法中,您可以精确地设置模拟对象的行为

EasyMock.expect(calcService.add(20.0, 10.0)).andReturn(30.0);
理解为“当调用
calcService.add()
时使用20和10,给出30”

最后,你:

  • 测试您的测试方法是否返回预期结果-
    assertXXX()
  • 测试calcService是否已被使用-
    verify()
  • 顺便说一句,代码

    Assert.assertEquals(mathApplication.subtract(20.0, 10.0), 10.0, 0);
    
    包含一个错误-请查看。应该是正确的

    Assert.assertEquals(10.0, mathApplication.subtract(20.0, 10.0), 0);
    
    它的工作原理几乎相同,只是如果测试不起作用,您将得到更正确的错误消息:

    Assertion error - expected 10.0, but was 11.0.
    
    不管怎么说,如果写为:

    double expected = 30.0;
    double actual = mathApplication.subtract(20.0, 10.0);
    Assert.assertEquals(expected, actual, 0.0000000001); // never expect exact floating point result
    

    为什么应该使用接口:这是一个很好的实践:)框架模拟接口比模拟类更容易(有些模拟框架甚至不能模拟类)。它引导您学习如何将接口与其实现分离,并编写更好的可测试类。

    它被称为依赖项注入&在《猜它是RTFM的提示》中有描述……非常有用的人。我知道密码是什么。我就是想不出注射部位是怎么出来的:\。我们为什么这么做?那么现在你明白了吗?:)顺便说一句,请阅读assert()的正确用法。我在代码中阅读了assert的其他内容。但是注入,noo:(你能帮我更多吗?@Hamid请查看我答案中的两个链接:Spring依赖注入和JavaEE CDI。你也可以搜索依赖注入的更详细解释。我添加了更多解释,但更多的解释应该使用外部源。
    double expected = 30.0;
    double actual = mathApplication.subtract(20.0, 10.0);
    Assert.assertEquals(expected, actual, 0.0000000001); // never expect exact floating point result