我像下面这样模拟一个抽象类:
myAbstractClass = Mockito.mock(MyAbstractClass.class, Mockito.CALLS_REAL_METHODS);
问题是MyAbstractClass
有一些依赖注入通过EJB
注释和没有setter。是否有一种注入依赖的方法?
@InjectMocks
不能与抽象类一起工作
我使用junit5。
我所做的是在@BeforeEach
中用new abstractClass()
实例化抽象类,并通过super
调用方法,如果方法不是抽象的(使用这个,因为我有protected
方法),在此之后,我使用ReflectionUtils.setField()
在抽象类中设置mock并测试每个方法并且工作得很好。我留下一个简单的例子。
AbstractClass
public abstract class AbstractClass {
@Autowired
private Environment environment;
protected String getProperty(String property){
return environment.getRequiredProperty(property);
}
}
AbstractClassTest
@ExtendWith(MockitoExtension.class)
class AbstractClassTest {
AbstractClass abstractClass;
@Mock
Environment environment;
@BeforeEach
void setUp() {
abstractClass = new AbstractClass() {
@Override
public String getProperty(String property) {
return super.getProperty(property);
}
};
ReflectionTestUtils.setField(abstractClass, "environment", environment);
}
@Test
void shouldReturnProperty() {
String propertyValue = "this property";
when(environment.getRequiredProperty("property")).thenReturn(propertyValue);
String property = abstractClass.getProperty("property");
assertEquals(propertyValue, property);
}
}
这只是使用mockit和junit5进行测试。请记住在使用new AbstractClass()
实例化类之后调用ReflectionUtils
,否则将不会注入模拟。
欢迎对该实现进行任何改进:d
由于无法实例化抽象类,因此没有什么可测试的。我建议您创建子类(它可以是测试类中的嵌套类),然后以这种方式运行测试。然后你可以像平常一样使用@Mock
, @InjectMocks
您可以使用Powermock库在myAbstractClass
中使用Whitebox.setInternalState(myAbstractClass, mock(MockedClass.class))
注入mock;
Junit 4的具体解决方案
需要测试的抽象类
@Slf4j
public abstract class AdhocNotificationEmail {
@Autowired
protected CustomerNotificationRepository customerNotificationRepository;
protected abstract Map<String, String> abstractMethod(AdhocNotificationDTO adhocNotificationDTO);
public JSONObject concreteMethod(){
// some stuff that needs to be tested and common to all subclasses
}
}
测试类:
@RunWith(SpringJUnit4ClassRunner.class)
public class AdhocNotificationEmailTest{
@Mock
protected CustomerNotificationRepository customerNotificationRepository;
private AdhocNotificationEmail unit;
@Before
public void setUp() {
unit = new AdhocNotificationEmail() {
@Override
protected Map<String, String> abstractMethod(AdhocNotificationDTO notificationDTO) {
return null;
}
};
unit.customerNotificationRepository = customerNotificationRepository;
}
@Test
public void concreteMethod_greenPath() {
final String templateName = "NOTIFICATION_TEMPLATE";
final AdhocNotificationDTO adhocNotificationDTOStub = getAdhocNotificationDTOStub(templateName);
final CustomerNotification customerNotificationStub = getCustomerNotificationStub(templateName);
when(customerNotificationRepository.findByIdAndTemplateName(id, templateName)).thenReturn(customerNotificationStub);
final JSONObject response = unit.concreteMethod(adhocNotificationDTOStub);
assertNotNull(response);
}
就我个人而言,我喜欢用@Before(或junit.jupiter中的@BeforeEach)中声明的匿名类来扩展抽象。这样我可以实现以下目标:
- 不要模拟我想测试的类(就像你正在做的
- 您可以为抽象类的依赖项提供mock,并且可以由非抽象方法调用;
- 你可以测试调用抽象方法的非抽象方法
Mockito.mock(MyAbstractClass.class, Mockito.CALLS_REAL_METHODS)
),因为那是一种反模式。你只想模拟你正在测试的类的依赖关系;的例子:
class TestAbstractClass {
@Mock
private ServiceDependency dependency;
private AbstractClass abstractClass;
@BeforeEach
void setUp() {
abstractClass= new AbstractClass (dependency) { };
}
@Test
void test(){
Mockito.when(dependency.someMethod()).thenReturn(something);
var result = abstractclass.someNonAbstractMethod();
// assertions
}
}
最好的做法是为所有可能从这个抽象类继承的类编写单元测试。因为理论上这是可能的情况。应该模拟这个依赖项,您应该忘记模拟这个EJB组件的依赖项。也许一些代码片段可以帮助您澄清这里要实现的目标。