使用 @InjectMocks 注入 String 属性
Posted
技术标签:
【中文标题】使用 @InjectMocks 注入 String 属性【英文标题】:Injecting a String property with @InjectMocks 【发布时间】:2016-07-05 05:14:19 【问题描述】:我有一个带有这个构造函数的 Spring MVC @Controller
:
@Autowired
public AbcController(XyzService xyzService, @Value("$my.property") String myProperty) /*...*/
我想为此控制器编写一个独立的单元测试:
@RunWith(MockitoJUnitRunner.class)
public class AbcControllerTest
@Mock
private XyzService mockXyzService;
private String myProperty = "my property value";
@InjectMocks
private AbcController controllerUnderTest;
/* tests */
有没有办法让@InjectMocks
注入我的字符串属性?我知道我不能模拟一个字符串,因为它是不可变的,但是我可以在这里注入一个普通的字符串吗?
@InjectMocks
在这种情况下默认注入一个空值。如果我把它放在myProperty
上,@Mock
会抛出异常,这是可以理解的。是否有另一个我错过的注释只是意味着“注入这个确切的对象而不是它的模拟”?
【问题讨论】:
我知道我可以在设置方法中手动实例化我的控制器,但我希望找到注释配置样式的解决方案。谢谢! 【参考方案1】:由于您使用的是 Spring,因此您可以使用来自 spring-test
模块的 org.springframework.test.util.ReflectionTestUtils
。它巧妙地包装了在对象上设置字段或在类上设置静态字段(以及其他实用方法)。
@RunWith(MockitoJUnitRunner.class)
public class AbcControllerTest
@Mock
private XyzService mockXyzService;
@InjectMocks
private AbcController controllerUnderTest;
@Before
public void setUp()
ReflectionTestUtils.setField(controllerUnderTest, "myProperty",
"String you want to inject");
/* tests */
【讨论】:
【参考方案2】:如果您不想更改代码,请使用 ReflectionTestUtils.setField 方法 How do I mock an autowired @Value field in Spring with Mockito?
【讨论】:
【参考方案3】:解决方案很简单:您应该为对象类型注入构造函数,而对于原始/最终依赖项,您可以简单地使用 setter 注入,这样就可以了。
所以这个:
public AbcController(XyzService xyzService, @Value("$my.property") String myProperty) /*...*/
将改为:
@Autowired
public AbcController(XyzService xyzService) /*...*/
@Autowired
public setMyProperty(@Value("$my.property") String myProperty)/*...*/
而测试中的@Mock
注入就像这样简单:
@Mock
private XyzService xyzService;
@InjectMocks
private AbcController abcController;
@BeforeMethod
public void setup()
MockitoAnnotations.initMocks(this);
abcController.setMyProperty("new property");
这就够了。去Reflections
是不可取的!
请尽可能避免在生产代码中使用反射!!
对于Jan Groot
的解决方案,我必须提醒您,这将变得非常讨厌,因为您将不得不删除所有@Mock
甚至@InjectMocks
,并且必须初始化然后手动注入它们以用于2个依赖项听起来很简单,但是对于 7 个依赖项,代码变成了一场噩梦(见下文)。
private XyzService xyzService;
private AbcController abcController;
@BeforeMethod
public void setup() // NIGHTMARE WHEN MORE DEPENDENCIES ARE MOCKED!
xyzService = Mockito.mock(XyzService.class);
abcController = new AbcController(xyzService, "new property");
【讨论】:
【参考方案4】:在这种情况下不要使用@InjectMocks。
做:
@Before
public void setup()
controllerUnderTest = new AbcController(mockXyzService, "my property value");
【讨论】:
【参考方案5】:你可以使用的是: org.mockito.internal.util.reflection.Whitebox
-
重构“AbcController”类构造函数
在您的测试类“之前”方法中,使用 Whitebox.setInternalState 方法指定您想要的任何字符串
@Before
public void setUp()
Whitebox.setInternalState(controllerUnderTest, "myProperty", "The string that you want");
【讨论】:
【参考方案6】:使用 Mockito 无法做到这一点,但 Apache Commons 实际上有一种方法可以使用其内置实用程序之一来做到这一点。您可以将它放在 JUnit 中的一个函数中,该函数在 Mockito 注入其余模拟之后但在您的测试用例运行之前运行,如下所示:
@InjectMocks
MyClass myClass;
@Before
public void before() throws Exception
FieldUtils.writeField(myClass, "fieldName", fieldValue, true);
【讨论】:
或者你可以只使用setter方法。任何方式魔法都会消失,在这个阶段甚至在 @Before 方法中创建 MyClass 也是一种选择:(【参考方案7】:你不能用 Mockito 做到这一点,因为正如你自己提到的,String
是 final
并且不能被模拟。
有一个@Spy
注释适用于真实 对象,但它具有与@Mock
相同的限制,因此您不能监视String
。
没有注释告诉 Mockito 只注入该值而不进行任何模拟或间谍活动。不过,这将是一个很好的功能。或许可以通过Mockito Github repository 提出建议。
如果您不想更改代码,则必须手动实例化控制器。
进行纯注解测试的唯一方法是重构控制器。它可以使用只包含一个属性的自定义对象,也可以使用具有多个属性的配置类。
@Component
public class MyProperty
@Value("$my.property")
private String myProperty;
...
这可以注入到控制器中。
@Autowired
public AbcController(XyzService xyzService, MyProperty myProperty)
...
然后你可以模拟和注入它。
@RunWith(MockitoJUnitRunner.class)
public class AbcControllerTest
@Mock
private XyzService mockXyzService;
@Mock
private MyProperty myProperty;
@InjectMocks
private AbcController controllerUnderTest;
@Before
public void setUp()
when(myProperty.get()).thenReturn("my property value");
/* tests */
这不是很简单,但至少您将能够拥有一个基于注释的纯测试,并带有一点存根。
【讨论】:
这很不幸。我查看了他们的问题工具,看起来他们已经discussed它before。一般的想法似乎是它超出了模拟框架的范围,甚至可能是代码味道,尽管我认为我不同意这一点。感谢您的帮助!以上是关于使用 @InjectMocks 注入 String 属性的主要内容,如果未能解决你的问题,请参考以下文章
如何在spring boot中将@Value属性从application.properties注入@InjectMocks?
在 Mockito Junit 中 @InjectMocks 后为空
Kotlin,Spring book,Mockito,@InjectMocks,使用与创建的不同的模拟