【问题标题】:How to make JMockit inject a specific instance to a JMockit @Tested class?如何让 JMockit 将特定实例注入 JMockit @Tested 类?
【发布时间】:2020-06-01 17:06:32
【问题描述】:

我有一门课程,我正在尝试使用 JMockit 1.49 进行测试。我的 Class-Under-Test 使用带有 2 个参数的构造函数注入;一个是模拟的,一个是我想在我的 Test 类中实例化并让 JMockit 为我注入的。不幸的是,我似乎无法让它工作。

public class DocFilterService {

    // get a static slf4j logger for the class
    protected static final Logger logger = getLogger(DocFilterService.class);

    private DocRepository DocRepository;
    private ObjectMapper objectMapper;

    @Inject
    public DocFilterService(DocRepository DocRepository, @Doc ObjectMapper objectMapper) {
        this.DocRepository = DocRepository;
        this.objectMapper = objectMapper;
    }

    // business method to test
    public DocWrapperDTO filterDoc(Context context, DocWrapperDTO dcfData){
      // some business logic looking to test.
      ... 
    }
}

在我的 JMockit 测试类中,我有以下内容:

public class DocFilterServiceTest2 {

    @Mocked @Injectable DocRepository DocRepository;
    @Doc ObjectMapper objectMapper = new DocProducer().getMapper();
    @Tested DocFilterService sut;

    @BeforeClass
    public static void jMockit(){
        Startup.verifyInitialization();
    }

    @Test
    public void testInvalidResponse() throws IOException {
        // create some valid input
        Context context = Context.ContextBuilder.aContext()
                .user(User.UserBuilder.aUser()
                        .id("123")
                        .build()
                )
                .build();

        ObjectMapper mapper = new ObjectMapper();
        new Expectations(){{
            DocRepository.filterDoc( (Body<DocWrapperDTO>) any ); result = mapper.readTree("{\"decision_id\": \"1\"}"); minTimes=1;
        }};


        // todo to fix.  Use a Mock repo and proper parameter
        sut.filterDoc( context, new DocWrapperDTO());

        new Verifications(){{
            DocRepository.filterDoc((Body<DocWrapperDTO>)any);
        }};

    }
}

使用此配置,JMockit 会抛出以下错误:

java.lang.IllegalArgumentException: No constructor in tested class that can be satisfied by available tested/injectable values
  public org.wada.adams.opa.dcf.DcfFilterService(org.wada.adams.opa.dcf.repository.DcfRepository, com.fasterxml.jackson.databind.ObjectMapper)
    disregarded because no tested/injectable value was found for parameter "objectMapper"

如果我将 @Tested 注释更改为

        @Tested(fullyInitialized=true) DocFilterService sut;

那么注入到我的sut 中的ObjectMapper 是ObjectMapper 的一个新实例,而不是声明为@Doc ObjectMapper objectMapper = new DocProducer().getMapper(); 实例的那个。

我尝试阅读 JMockit 上的文档,我的印象是它应该使用在我的 Test 类中声明的任何实例,但它似乎没有这样做。

如何向 JMockit 指定/声明我希望它使用我的实例作为注入候选对象而不实例化一个新实例?我需要将其声明为@Tested 实例吗?它似乎不是正确的注释。

【问题讨论】:

  • 我什至尝试通过使用 MockUp 为某些类提供“自定义”实现来解决这个问题,同时仍然让 JMockit 认为它是一个可注入的模拟。但是,这似乎也不支持。 github.com/jmockit/jmockit1/issues/211。看来你在这里运气不好。也许您可以尝试使用自己的构造函数的 DocFilterService 子类,该构造函数只公开需要模拟/注入的字段。它可能只是测试文件中的静态嵌套类。哈克,当然,但从技术上讲,它正在测试相同的代码,同时仍然允许您使用 new DocProducer().getMapper()

标签: java jmockit


【解决方案1】:

对于@Tested 元素,JMockit 将调用它具有@Injectable 参数的构造。在您的情况下,您需要同时拥有 DocRepository 和 ObjectMapper 作为@Injectable。默认情况下,@Injectables 也是@Mocked,所以你不需要两个注解。我相信这会奏效

@Injectable DocRepository DocRepository;
@Injectable ObjectMapper objectMapper;
@Tested DocFilterService sut;

也就是说,上面的内容使 objectMapper 成为一个模拟对象。您的原始代码似乎正在尝试使用它的真实实例。简单地说,这是一个坏主意,因为 ObjectMapper 是一个不同的“单元”,所以在 DocFilterService 的“单元”中,我们应该将其视为一个黑盒并模拟它。

【讨论】:

    猜你喜欢
    • 2023-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多