0

New to Spring, I am building a toy application that represents a car dealership. Here's my interface for this service:

public interface DealershipService {

  Long getRevenue();

  void sell(Car car);

  void buy(Car car);

  List<Car> findByMake(String make);

  List<Car> findByMakeAndModel(String make, String model);

  List<Car> findByMakeModelAndYear(String make, String model, String year);

  List<Car> findByYearRange(String yearLowInclusive, String yearHighInclusive);

  List<Car> findInCostRange(long costLowInclusive, long costHighInclusive);
}

And here is a small part of the implementation (really all we need is the field revenue).

@Slf4j
@Data
@Service
public class DealershipServiceImpl implements DealershipService {

  @Value("${revenue}")
  private Long revenue;
  private final CarRepository carRepository;

  @Autowired
  public DealershipServiceImpl(CarRepository carRepository) {
    this.carRepository = carRepository;
  }

  @Override
  public void sell(Car car) {
    carRepository.addToRepository(car);
    revenue += car.getCostInUSD();
    log.info("Sold car {}. Current revenue: {}", car, revenue);
  }

.
.
.

The revenue variable is in my application.properties as:

revenue = 1000000

Because I want the default revenue for this car dealership to be 1000000 USD. Now I'm trying to write a test.

@SpringJUnitConfig(classes = TestConfig.class)
public class DealershipServiceIT {

    @Autowired
    private  DealershipService dealershipService;

    @Test
    public void whenNoTransactions_TotalRevenueIsAMil(){
        assertThat(dealershipService.getRevenue(), is(1_000_000L));
    }
}

The TestConfig class that the above unit test class points to:

@Configuration
@ComponentScan("com.jason.cardealership")
public class TestConfig {
}

My class' central @SpringBootApplication class is this, and the context loads just fine when I run it:

@SpringBootApplication
public class CarDealershipApplication {

  public static void main(String[] args) {
    SpringApplication.run(CarDealershipApplication.class, args);
  }
}

But when I run the test method whenNoTransactions_TotalRevenueIsAMil() I get a sequence of exceptions, the bottom one of which is:

Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'revenue' in value "${revenue}"
    at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:180)
    at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
    at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:239)
    at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:210)
    at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.lambda$processProperties$0(PropertySourcesPlaceholderConfigurer.java:191)
    at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:920)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1346)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1325)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:709)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:692)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:133)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:481)
    ... 87 common frames omitted

I also added @PropertySource("classpath:application.properties") on top of my testing class after consulting this post, but without success.

How can I read the value of that variable from within my test?

Jason
  • 2,495
  • 4
  • 26
  • 37

2 Answers2

2

Few things you can check:

  1. There is a separate application.properties file for test cases. The "revenue" property should be present in that file as well.
  2. You can also use "@SpringBootTest" annotation for test class.
pcsutar
  • 1,715
  • 2
  • 9
  • 14
0

Because using @SpringJUnitConfig will just start the spring context in a traditional way but not in the spring-boot way. And those externalise properties to the application.properties feature is the spring-boot feature and hence it does not work when the test is started in the non-boot way.

To fix the problem , you can add ConfigDataApplicationContextInitializer in @SpringJUnitConfig :

@SpringJUnitConfig(classes = TestConfig.class,initializers={ConfigDataApplicationContextInitializer.class})
public class DealershipServiceIT {



}

Or start the test in spring-boot way :

@SpringBootTest(classes = TestConfig.class)
public class DealershipServiceIT {


}

Ken Chan
  • 84,777
  • 26
  • 143
  • 172
  • `@SpringBootTest`, in conjunction with having another `application.properties` under `src/test/resources`, worked! Thanks. – Jason Mar 04 '23 at 20:08