使用 JMockit 模拟自动装配接口实现

发布于 2024-08-17 02:51:12 字数 639 浏览 12 评论 0原文

我们正在为一个类编写 JUnit 测试,该类使用 Spring 自动装配来注入依赖项,该依赖项是接口的某个实例。由于被测试的类从未显式实例化依赖项或将其传递到构造函数中,因此 JMockit 似乎也没有义务实例化它。

到目前为止,我们一直在使用 SpringRunner 来为我们加载 Spring 模拟依赖项,这很有效。我们不喜欢的两件事是:1)每次运行测试时都必须加载并初始化 Spring 框架,这并不快;2)我们被迫将所有模拟依赖项显式创建为真实类,这会导致JMockit 有助于消除。

下面是我们正在测试的一个简化示例:

public class UnitUnderTest {

   @Autowired
   ISomeInterface someInterface;

   public void callInterfaceMethod() {

      System.out.println( "UnitUnderTest.callInterfaceMethod calling someInterface.doSomething");
      someInterface.doSomething();
   }

}

那么,问题是,有没有办法让 JMockit 创建一个模拟 someInterface

We are writing JUnit tests for a class that uses Spring autowiring to inject a dependency which is some instance of an interface. Since the class under test never explicitly instantiates the dependency or has it passed in a constructor, it appears that JMockit doesn't feel obligated to instantiate it either.

Up until now we have been using SpringRunner to have Spring load mock dependencies for us, which works. Two things we don't like about this are 1) the Spring framework has to be loaded and initialized each time running the tests which is not exactly speedy, and 2) we are forced to explicitly create all mock dependencies as real classes, something which JMockit helps eliminate.

Here's a simplified example of what we're testing:

public class UnitUnderTest {

   @Autowired
   ISomeInterface someInterface;

   public void callInterfaceMethod() {

      System.out.println( "UnitUnderTest.callInterfaceMethod calling someInterface.doSomething");
      someInterface.doSomething();
   }

}

So, the question is, is there a way to have JMockit create a mock someInterface?

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

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

发布评论

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

评论(5

我恋#小黄人 2024-08-24 02:51:12

JMockit 将始终实例化一个模拟接口(最终模拟字段的情况除外),但这仅发生在测试代码中。它不会自动将实例注入到被测试的代码中。

您必须手动注入模拟实例。例如:

public class SomeTest
{
   @Autowired UnitUnderTest unitUnderTest;
   @Mocked ISomeInterface theMock; // created and assigned automatically

   @Test
   public void testSomeMethod()
   {
      Deencapsulation.setField(unitUnderTest, theMock);
      //proceed with unit test here
   }
}

mockit.Deencapsulation 是一个基于反射的实用程序类,可让您调用私有方法、获取/设置字段等。

JMockit will always instantiate a mocked interface (except in the case of a final mock field), but that only occurs in test code. It will not automatically inject the instance into code under test.

You would have to manually inject the mock instance. For example:

public class SomeTest
{
   @Autowired UnitUnderTest unitUnderTest;
   @Mocked ISomeInterface theMock; // created and assigned automatically

   @Test
   public void testSomeMethod()
   {
      Deencapsulation.setField(unitUnderTest, theMock);
      //proceed with unit test here
   }
}

mockit.Deencapsulation is a Reflection-based utility class that lets you invoke private methods, get/set fields, etc.

病毒体 2024-08-24 02:51:12

您可以使用 org.springframework.test.util.ReflectionTestUtils 在测试用例中显式注入模拟的 ISomeInterface 。

请参阅文档

You can use org.springframework.test.util.ReflectionTestUtils to explicitly inject your mocked ISomeInterface in your test case.

See documentation

メ斷腸人バ 2024-08-24 02:51:12

有了上面提供的提示,对于刚接触 JMockit 的人来说,以下是我发现最有用的内容:JMockit 提供了 Deencapsulation 类,允许您设置私有依赖字段的值(无需拖动 Spring库),以及 MockUp 类,它允许您显式创建接口的实现并模拟接口的一个或多个方法。以下是我最终解决这个特殊案例的方法:

@Before
public void setUp() {

   IMarketMakerDal theMock = new MockUp <IMarketMakerDal>() {

      @Mock
      MarketMakerDcl findByMarketMakerGuid( String marketMakerGuid ) {

         MarketMakerDcl marketMakerDcl = new MarketMakerDcl();
         marketMakerDcl.setBaseCurrencyCode( CURRENCY_CODE_US_DOLLAR );
         return marketMakerDcl;
      }
   }.getMockInstance();

   setField( unitUnderTest, theMock );
}

感谢大家的帮助。

With the hints kindly provided above, here's what I found most useful as someone pretty new to JMockit: JMockit provides the Deencapsulation class to allow you to set the values of private dependent fields (no need to drag the Spring libraries in), and the MockUp class that allows you to explicitly create an implementation of an interface and mock one or more methods of the interface. Here's how I ended up solving this particular case:

@Before
public void setUp() {

   IMarketMakerDal theMock = new MockUp <IMarketMakerDal>() {

      @Mock
      MarketMakerDcl findByMarketMakerGuid( String marketMakerGuid ) {

         MarketMakerDcl marketMakerDcl = new MarketMakerDcl();
         marketMakerDcl.setBaseCurrencyCode( CURRENCY_CODE_US_DOLLAR );
         return marketMakerDcl;
      }
   }.getMockInstance();

   setField( unitUnderTest, theMock );
}

Thanks everyone for the help.

糖粟与秋泊 2024-08-24 02:51:12

出错的人

java.lang.IllegalStateException: Missing @Injectable for field *** 

遇到或

java.lang.IllegalStateException: Missing @Tested class for field ***

对于那些在使用jmockit模拟spring(或spring boot)中的@autowired字段时 框架,我做了以下两个步骤来避免上述错误:

  1. 使用 @Tested(completeInitialized=true) 而不是 @Tested

https://groups.google.com /forum/#!msg/jmockit-users/uo0S51lSX24/lQhLNN--eJcJ

  1. 将 jmockit 的版本恢复到 1.18 或之前的版本

https://groups.google.com/forum/#!主题/jmockit-users/wMFZggsA8LM

For those people who met

java.lang.IllegalStateException: Missing @Injectable for field *** 

or

java.lang.IllegalStateException: Missing @Tested class for field ***

error when using jmockit to mock @autowired field in spring ( or spring boot) framework, I did below two steps to avoid above errors:

  1. use @Tested(fullyInitialized=true) instead of @Tested

https://groups.google.com/forum/#!msg/jmockit-users/uo0S51lSX24/lQhLNN--eJcJ

  1. revert jmockit's version back to 1.18 or previous ones

https://groups.google.com/forum/#!topic/jmockit-users/wMFZggsA8LM

就此别过 2024-08-24 02:51:12

如果接口有 @Qualifier 注释,则需要将 @Injectable 字段命名为限定符中的名称。

以下是 JMockit 文档 的引用:

在查找匹配的 @Injectable 或 @Tested 值时,将使用 Java EE 字段注释(@Resource(name)、@Named)或 Spring 框架 (@Qualifier) 中指定的自定义名称。当此类名称包含 -(破折号)或 . (点)字符,则使用相应的驼峰式名称。

例如:

@Component
public class AClass {

   @Autowired
   private Bean1 bean1;

   @Autowired
   @Qualifier("my-dashed-name")
   private AmqpTemplate rpcTemplate;
}

单元测试类:

public class AClassTest {

   @Injectable
   private Bean1 bean1;

   @Injectable
   private AmqpTemplate myDashedName;

   @Tested
   private AClass aClass = new AClass();
}

也不需要为每个 @Autowired bean 使用 setFiled,所有字段都会在 @Tested 类实例化时自动注入。在 JMockit 版本上测试1.30

If you have a @Qualifier annotation for the interface, you need to name your @Injectable field exactly as it is named in qualifier.

Here is quote from JMockit doc:

Custom names specified in field annotations from Java EE (@Resource(name), @Named) or the Spring framework (@Qualifier) are used when looking for a matching @Injectable or @Tested value. When such a name contains a - (dash) or . (dot) character, the corresponding camel-cased name is used instead.

For example:

@Component
public class AClass {

   @Autowired
   private Bean1 bean1;

   @Autowired
   @Qualifier("my-dashed-name")
   private AmqpTemplate rpcTemplate;
}

Unit test class:

public class AClassTest {

   @Injectable
   private Bean1 bean1;

   @Injectable
   private AmqpTemplate myDashedName;

   @Tested
   private AClass aClass = new AClass();
}

Also there is no need to use setFiled for each @Autowired bean, all fields injects automatically when @Tested class instantiated. Tested on JMockit ver. 1.30

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