使用Spring进行Java单元测试时,用于模拟目的的接口。

huangapple 未分类评论43阅读模式
英文:

Using an interface for mocking purposes in unit tests for Java with Spring

问题

我在尝试使用基于Spring的API中模拟接口时遇到了问题。我得到了这个错误:

描述:

构造函数中的Service的参数0需要一个类型为'bm.app.services.DiscountProvider'的bean,但找不到该bean。


操作:

考虑在配置中定义一个类型为'bm.app.services.DiscountProvider'的bean。

而我实在是不明白。我正在将一个接口(DiscountProvider)作为我的服务类(Service)构造函数的参数添加进去,以便于使用Mockito进行测试。

我的Service的开头部分(我认为这是唯一相关的部分)如下所示:

@org.springframework.stereotype.Service
public class Service {

    DiscountProvider discountProvider;

    public Service(DiscountProvider discountProvider) {
        this.discountProvider = discountProvider;
    }

接口如下所示:

import org.springframework.stereotype.Component;

import java.math.BigDecimal;

@Component
public interface DiscountProvider {

    BigDecimal getThePriceOfTheProduct();
}

而我的测试类如下所示:

@Component
public class ServiceTests {

    Service service;
    DiscountProvider discountProvider;

    @Test
    @DisplayName("应返回比原价高十个百分点的值。")
    void shouldReturnValueHigherByTenPercent() {
        //给定
        Mockito.when(discountProvider.getThePriceOfTheProduct()).thenReturn(new BigDecimal("20.00"));
        AdditionalMiniProduct product = new AdditionalMiniProduct(UUID.randomUUID(), new BigDecimal("300.00"), "Yellow Loan");
        //当
        BigDecimal result = service.increaseByGivenAmount(product.getNetPrice(), discountProvider.getThePriceOfTheProduct());
        //那么
        assertThat(result).isEqualTo("315.00");
    }

    @Test
    void shouldReturnACorrectRiskLevelValue() {
        int months = 18;
        assertEquals(5, calculateRiskLevelByNumberOfMonths(months));
    }

    @Test
    void shouldReturnACorrectEnumValueBasedOnNumberOfMonths() {
        int months = 26;
        assertEquals(RiskLevel.HIGH, giveTheRiskNameBasedOnNumberOfMonths(months));
    }

    @Test
    void shouldReturnAPriceOfAProvidedFinancialProduct() {
        BigDecimal productPrice = new BigDecimal(1200);
        String productName = "SilverLoan";
        assertEquals(productPrice,findAProductPriceByGivenName(productName));
    }

    @BeforeEach
    void setUp() {
        service = new Service(discountProvider);
        discountProvider = Mockito.mock(DiscountProvider.class);
    }
}

现在,我在测试方面相对比较新,所以我确实预计可能会有一些初级错误。问题在于,这是我第一次尝试在我的Spring应用程序中运行更"复杂"的单元测试,而我就是搞不明白。它拒绝承认@Component注释(或者我找到的任何其他注释)。

英文:

I have a problem trying to mock interfaces in my API based on Spring. I am getting this error:

Description:

Parameter 0 of constructor in bm.app.services.Service required a bean of type 'bm.app.services.DiscountProvider' that could not be found.


Action:

Consider defining a bean of type 'bm.app.services.DiscountProvider' in your configuration.

And I just don't understand it. I am adding an interface (DiscountProvider) as a parameter for a constructor of my service class (Service) in order to use Mockito for testing purposes.

The beginning of my Service (I think the only relevant part here) looks like this:

@org.springframework.stereotype.Service
public class Service {

    DiscountProvider discountProvider;

    public Service(DiscountProvider discountProvider) {
        this.discountProvider = discountProvider;
    }

The interface looks like this:

import org.springframework.stereotype.Component;

import java.math.BigDecimal;

@Component
public interface DiscountProvider {

    BigDecimal getThePriceOfTheProduct();
}

And my test class looks like that:

@Component
public class ServiceTests {

    Service service;
    DiscountProvider discountProvider;

    @Test
    @DisplayName("Should return the value higher by ten percent.")
    void shouldReturnValueHigherByTenPercent() {
        //given
        Mockito.when(discountProvider.getThePriceOfTheProduct()).thenReturn(new BigDecimal("20.00"));
        AdditionalMiniProduct product = new AdditionalMiniProduct(UUID.randomUUID(), new BigDecimal("300.00"), "Yellow Loan");
        //when
        BigDecimal result = service.increaseByGivenAmount(product.getNetPrice(), discountProvider.getThePriceOfTheProduct());
        //then
        assertThat(result).isEqualTo("315.00");
    }

    @Test
    void shouldReturnACorrectRiskLevelValue() {
        int months = 18;
        assertEquals(5, calculateRiskLevelByNumberOfMonths(months));
    }

    @Test
    void shouldReturnACorrectEnumValueBasedOnNumberOfMonths() {
        int months = 26;
        assertEquals(RiskLevel.HIGH, giveTheRiskNameBasedOnNumberOfMonths(months));
    }

    @Test
    void shouldReturnAPriceOfAProvidedFinancialProduct() {
        BigDecimal productPrice = new BigDecimal(1200);
        String productName = "SilverLoan";
        assertEquals(productPrice,findAProductPriceByGivenName(productName));
    }

    @BeforeEach
    void setUp() {
        service = new Service(discountProvider);
        discountProvider = Mockito.mock(DiscountProvider.class);
    }


}

Now, I am fairly new to testing, so I do expect to have made some rookie mistakes. The thing is, that's my first attempt to have more "complex" unit tests run with one of my Spring apps and I just don't get it. It refuses to acknowledge the @Component (or any other I managed to find) annotation.

答案1

得分: 0

如果您正在对 `Service` 类进行单元测试,应该模拟该测试类的所有外部依赖。

假设您正在使用 JUnit 5,可能的测试设置如下:

    @ExtendWith(MockitoExtension.class)
    public class ServiceTest {
    
      @Mock
      private DiscountProvider discountProvider;
    
      @InjectMocks
      private Service service;
    
      @Test
      @DisplayName("应返回比例增加十个百分点的值。")
      void shouldReturnValueHigherByTenPercent() {
        //given
        Mockito.when(discountProvider.getThePriceOfTheProduct()).thenReturn(new BigDecimal("20.00"));
        AdditionalMiniProduct product = new AdditionalMiniProduct(UUID.randomUUID(), new BigDecimal("300.00"), "Yellow Loan");
         
        //when
        BigDecimal result = service.increaseByGivenAmount(product.getNetPrice(), discountProvider.getThePriceOfTheProduct());

        //then
        assertThat(result).isEqualTo("315.00");
      }
    
    }

另外,您的测试类不需要使用 `@Component`。这个注解用于您的 _生产_ 代码,用于标记 Spring bean。
英文:

If you are unit testing your Service class, you should mock all external dependencies of this class under test.

Assuming you are using JUnit 5, a possible test setup can look like the following

@ExtendWith(MockitoExtension.class)
public class ServiceTest {

  @Mock
  private DiscountProvider discountProvider;

  @InjectMocks
  private Service service;

  @Test
  @DisplayName("Should return the value higher by ten percent.")
  void shouldReturnValueHigherByTenPercent() {
    //given
    Mockito.when(discountProvider.getThePriceOfTheProduct()).thenReturn(new BigDecimal("20.00"));
    AdditionalMiniProduct product = new AdditionalMiniProduct(UUID.randomUUID(), new BigDecimal("300.00"), "Yellow Loan");
     
    //when
    BigDecimal result = service.increaseByGivenAmount(product.getNetPrice(), discountProvider.getThePriceOfTheProduct());

    //then
    assertThat(result).isEqualTo("315.00");
  }

}

In addition, you don't need @Component for your test classes. This annotation is used for your production code to mark Spring beans.

huangapple
  • 本文由 发表于 2020年7月27日 00:56:03
  • 转载请务必保留本文链接:https://java.coder-hub.com/63103118.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定