使用mockito将mock注入抽象类



我像下面这样模拟一个抽象类:

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)中声明的匿名类来扩展抽象。这样我可以实现以下目标:

    不要模拟我想测试的类(就像你正在做的Mockito.mock(MyAbstractClass.class, Mockito.CALLS_REAL_METHODS)),因为那是一种反模式。你只想模拟你正在测试的类的依赖关系;
  • 您可以为抽象类的依赖项提供mock,并且可以由非抽象方法调用;
  • 你可以测试调用抽象方法的非抽象方法

的例子:

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组件的依赖项。也许一些代码片段可以帮助您澄清这里要实现的目标。

相关内容

  • 没有找到相关文章

最新更新