0

I have a Spring Boot REST API application that uses different profiles like dev, test, prod, etc

It also makes a call to another API to retrieve some data like:

Client ---> MYAPI ---> DOWNSTREAMAPI

MyAPI is working fine and it will get data from DOWNSTREAMAPI and return it to client.

However, for client e2e testing purposes, I would like to be able to return it an isolated set of data so when client calls MYAPI, it will always get this set of data.

So, this is different from unit testing or integration testing of MYAPI. This is to build MYAPI so it gets real data from DOWNSTREAMAPI if active profile is set to "prod" and to get mocked data if active profile is "dev" so when client calls MYAPI running in "dev" mode, this mocked data will be returned to it. This will ensure that client e2d tests will always operate on isolated test data that does not change.

This data is not coming from a database but from another API.

I was not able to find a suggestion how to design this. All I was finding is how to mock services from within unit tests or integration tests but this is not what I am doing here.

So, I came up with my own solution which looks like below

Controller

My controller calls HelloService which has different implementation based on currently set active profile

@RestController
@RequestMapping("/testdatamocking")
public class HelloController {

    @Autowired
    private HelloService helloService;

    @GetMapping("/hello")
    public String hello() {
        return helloService.hello();
    }
}

Services

HelloService interface:

public interface HelloService {
    String hello();
}

HelloService implementations:

@Service
@Profile("!dev")
public class RealHelloServiceImpl implements HelloService{

    @Override
    public String hello() {
        return "real hello";
    }
}


@Service
@Profile("dev")
public class MockHelloServiceImpl implements HelloService {
    @Override
    public String hello() {
        return "mocked hello";
    }
}

I have different application properties files:

  • application.properties
  • application-prod.properties
  • application-dev.properties
  • application-test.properties

The HelloService implementations are annotated with @Profile which determines which implementation will be created in spring context based on the currently used spring active profile in application.properties.

If my application.properties sets spring.profiles.active=dev, then MockHelloServiceImpl will be created.

Otherwise, is my application.properties set spring.profiles.active to some other profile, then RealHelloServiceImpl will be created.

UPDATE

I would like to expand on this so that I can return different data based on the provided request body. This is because this will be used in Cucumber/Cypress e2e tests on client side.

A client could send a POST request with body like:

{"id":2, "type":1}

For this POST request, response could contain a JSON body containing data for item with id=2 and of type=1.

As the id and type can change in this request, different data would need to be returned. As this is data for testing purposes, I want to make it non-volatile in order to produce consistent tests, but I am not sure what would be a good way and how to create this non-volatile data to be returned.

Would it be just hardcoded list of items which can be modified to add new items for new test cases?

Or an in-memory H2 database containing the items or something else?

2
  • 1
    I have voted to close the question because I think StackOverflow would be a better site to make this kind of questions. Consider moving the question to SO to get more feedback and answers.
    – Laiv
    Aug 25, 2022 at 9:53
  • I added an update which explains why I did not post this to SO. Let me know
    – dbnex14
    Aug 25, 2022 at 16:13

1 Answer 1

1

The use of @Profile was designed exactly for the purpose you had in mind - conditional beans based on active profiles. So it sounds like you've discovered the canonical answer.

However, if you want more fine-grained control than profiles, you can opt for feature-flags (toggles) using conditional beans. This is particularly useful if you have multiple configurations which should/should not be enabled based on a variety of factors. At the most basic level, you can implement them in a profile-based style similar to the approach that you've suggested.

Example

Properties

# application.properties
feature.flag.mockHelloService = false

# application-dev.properties
feature.flag.mockHelloService = true

Controller - Leave as-is

Services

@Service
@ConditionalOnProperty(name = "feature.flag.mockHelloService", havingValue = "false")
public class RealHelloServiceImpl implements HelloService{
    // ...
}


@Service
@ConditionalOnProperty(name = "feature.flag.mockHelloService", havingValue = "true")
public class MockHelloServiceImpl implements HelloService {
    // ...
}

Addressing the Update

If the mocked data to be returned is conditional on the request body, then there are a number of different ways to keep track of your mock responses, ranging from a switch returning hardcoded values, to a database of the form param 1 | param 2 | ... | json . It really depends on how complex your conditional logic is. I really like to place mock responses in a resources folder. I use a naming convention something like [url].[type], but you can adapt that to your needs. For example:

  • resources/
    • mock-endpoints/
      • root-url1.json
      • root-url2.json
      • sub/
        • url.json

Sticking to a convention allows me to generate a file path from the URL, which allows me to focus on getting both the branching logic and the mock response right, rather than how those responses are generated.

3
  • Thank you for sharing the information on feature flags, I will definitely take a note of that as I was not aware and see when and where I could use it. I updated my question as it was not complete, if you dont mind, taking a look at the update, that would be greatly appreciated and thanks again.
    – dbnex14
    Aug 25, 2022 at 16:17
  • Updated. Let me know if this helps. Aug 25, 2022 at 17:12
  • Would you mind elaborating bit more on your idea with mock responses in resources folder. I assume you are capturing real responses somehow, then copying their jsons into resources folder? Also, how are you handling these in your code?
    – dbnex14
    Aug 29, 2022 at 15:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.