How to implement an integration test to check if my circuit breaker fallback is called? - testing

In my application, I need to call an external endpoint and if it is too slow a fallback is activated.
The following code is an example of how my app looks like:
#FeignClient(name = "${config.name}", url = "${config.url:}", fallback = ExampleFallback.class)
public interface Example {
#RequestMapping(method = RequestMethod.GET, value = "/endpoint", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
MyReturnObject find(#RequestParam("myParam") String myParam);
}
And its fallback implementation:
#Component
public Class ExampleFallback implements Example {
private final FallbackService fallback;
#Autowired
public ExampleFallback(final FallbackService fallback) {
this.fallback = fallback;
}
#Override
public MyReturnObject find(final String myParam) {
return fallback.find(myParam);
}
Also, a configured timeout for circuit breaker:
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000
How can I implement an integration test to check if my circuit break is working, i.e, if my endpoint (mocked in that case) is slow or if it returns an error like 4xx or 5xx?
I'm using Spring Boot 1.5.3 with Spring Cloud (Feign + Hystrix)

Note i donot know Feign or Hystrix.
In my opinion it is problematic to implement an automated integrationtest that simulates different implementatondetails of Feign+Hystrix - this implementation detail can change at any time. There are many different types of failure: primary-Endpoint not reachable, illegal data (i.e. receiving a html-errormessage, when exprecting xml data in a special format), disk-full, .....
if you mock an endpoint you make an assumption of implementationdetail of Feign+Hystrix how the endpoint behaves in a errorsituation (i.e. return null, return some specific errorcode, throw an exception of type Xyz....)
i would create only one automated integration test with a real primary-enpoint that has a never reachable url and a mocked-fallback-endpoint where you verify that the processed data comes from the mock.
This automated test assumes that handling of "networkconnection too slow" is the same as "url-notfound" from your app-s point of view.
For all other tests i would create a thin wrapper interface around Feign+Hystrix where you mock Feign+Hystrix. This way you can automatically test for example what happens if you receive 200bytes from primary interface and then get an expetion.
For details about hiding external dependencies see onion-architecture

Related

Integration testing with event sourcing systems

I'm working on a PoC where we use CQRS in combination with Event Sourcing. We use the Axon framework and Axon server as toolset.
We have some microservices (Maven packages) with some business logic.
A simple overview of the application flow:
We post a xml message (with REST) to service 1 that will result in an event (with Aggregate).
Service 2 handles the event "fired" by service 1 and starts a saga flow. Part of the sage flow is for example to send a mail message.
I can do some tests with Axon Test to test the aggregate from service 1 or the saga from service 2. But is there a good option to do a real integration test where we start with posting a message to the REST interface and check all the operations in the aggregate and saga (inclusive sending mail and so on)
Maybe this kind of integration test is overdone and it's better to test each component on it's own. I doubt what's needed / the best solution to test this type of system.
I suggest to have a look at testcontainers (https://www.testcontainers.org/)
It provides a very convenient way to start up and cleanly tear down docker containers in JUnit tests. This feature is very useful for integration testing of applications against real databases and any other resource (for example Axon Server) for which a docker image is available (https://hub.docker.com/r/axoniq/axonserver/).
I'm sharing some code snippets from JUnit 4 test class (Kotlin). Hopefully this can help you to get started and evolve your specific test strategy (integration should cover smaller scope then end-to-end tests). My opinion is that integration test should focus on Axon messaging API components and REST API components separately/independently. End-to-end should cover all components in your microservice/s.
#RunWith(SpringRunner::class)
#SpringBootTest
#ContextConfiguration(initializers = [DrestaurantCourierCommandMicroServiceIT.Initializer::class])
internal class DrestaurantCourierCommandMicroServiceIT {
#Autowired
lateinit var eventStore: EventStore
#Autowired
lateinit var commandGateway: CommandGateway
companion object {
// An Axon Server container
#ClassRule
#JvmField
var axonServerTestContainer = KGenericContainer(
"axoniq/axonserver")
.withExposedPorts(8024, 8124)
.waitingFor(Wait.forHttp("/actuator/info").forPort(8024))
.withStartupTimeout(Duration.of(60L, ChronoUnit.SECONDS))
// A PostgreSQL container is being started up using a JUnit Class Rule which gets triggered before any of the tests are run:
#ClassRule
#JvmField
var postgreSQLContainer = KPostgreSQLContainer(
"postgres:latest")
.withDatabaseName("drestaurant")
.withUsername("demouser")
.withPassword("thepassword")
.withStartupTimeout(Duration.of(60L, ChronoUnit.SECONDS))
}
// Pass details on the application as properties BEFORE Spring starts creating a test context for the test to run in:
class Initializer : ApplicationContextInitializer<ConfigurableApplicationContext> {
override fun initialize(configurableApplicationContext: ConfigurableApplicationContext) {
val values = TestPropertyValues.of(
"spring.datasource.url=" + postgreSQLContainer.jdbcUrl,
"spring.datasource.username=" + postgreSQLContainer.username,
"spring.datasource.password=" + postgreSQLContainer.password,
"spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect",
"axon.axonserver.servers=" + axonServerTestContainer.containerIpAddress + ":" + axonServerTestContainer.getMappedPort(8124)
)
values.applyTo(configurableApplicationContext)
}
}
#Test
fun `restaurant command microservice integration test - happy scenario`() {
val who = "johndoe"
val auditEntry = AuditEntry(who, Calendar.getInstance().time)
val maxNumberOfActiveOrders = 5
val name = PersonName("Ivan", "Dugalic")
val orderId = CourierOrderId("orderId")
// ******* Sending the `createCourierCommand` ***********
val createCourierCommand = CreateCourierCommand(name, maxNumberOfActiveOrders, auditEntry)
commandGateway.sendAndWait<Any>(createCourierCommand)
await withPollInterval org.awaitility.Duration.ONE_SECOND atMost org.awaitility.Duration.FIVE_SECONDS untilAsserted {
val latestCourierCreatedEvent = eventStore.readEvents(createCourierCommand.targetAggregateIdentifier.identifier).asStream().toList().last().payload as CourierCreatedEvent
assertThat(latestCourierCreatedEvent.name).isEqualTo(createCourierCommand.name)
assertThat(latestCourierCreatedEvent.auditEntry.who).isEqualTo(createCourierCommand.auditEntry.who)
assertThat(latestCourierCreatedEvent.maxNumberOfActiveOrders).isEqualTo(createCourierCommand.maxNumberOfActiveOrders)
}
// ******* Sending the `createCourierOrderCommand` **********
val createCourierOrderCommand = CreateCourierOrderCommand(orderId, auditEntry)
commandGateway.sendAndWait<Any>(createCourierOrderCommand)
await withPollInterval org.awaitility.Duration.ONE_SECOND atMost org.awaitility.Duration.FIVE_SECONDS untilAsserted {
val latestCourierOrderCreatedEvent = eventStore.readEvents(createCourierOrderCommand.targetAggregateIdentifier.identifier).asStream().toList().last().payload as CourierOrderCreatedEvent
assertThat(latestCourierOrderCreatedEvent.aggregateIdentifier.identifier).isEqualTo(createCourierOrderCommand.targetAggregateIdentifier.identifier)
assertThat(latestCourierOrderCreatedEvent.auditEntry.who).isEqualTo(createCourierOrderCommand.auditEntry.who)
}
// ******* Assign the courier order to courier **********
val assignCourierOrderToCourierCommand = AssignCourierOrderToCourierCommand(orderId, createCourierCommand.targetAggregateIdentifier, auditEntry)
commandGateway.sendAndWait<Any>(assignCourierOrderToCourierCommand)
await withPollInterval org.awaitility.Duration.ONE_SECOND atMost org.awaitility.Duration.FIVE_SECONDS untilAsserted {
val latestCourierOrderAssignedEvent = eventStore.readEvents(assignCourierOrderToCourierCommand.targetAggregateIdentifier.identifier).asStream().toList().last().payload as CourierOrderAssignedEvent
assertThat(latestCourierOrderAssignedEvent.aggregateIdentifier.identifier).isEqualTo(assignCourierOrderToCourierCommand.targetAggregateIdentifier.identifier)
assertThat(latestCourierOrderAssignedEvent.auditEntry.who).isEqualTo(assignCourierOrderToCourierCommand.auditEntry.who)
assertThat(latestCourierOrderAssignedEvent.courierId.identifier).isEqualTo(assignCourierOrderToCourierCommand.courierId.identifier)
}
}
}
class KGenericContainer(imageName: String) : GenericContainer<KGenericContainer>(imageName)
class KPostgreSQLContainer(imageName: String) : PostgreSQLContainer<KPostgreSQLContainer>(imageName)
Axon developers suggest to go with docker solution as mentioned here.
Testcontainers seems to be the best here.
My java snippet:
#ActiveProfiles("test")
public class TestContainers {
private static final int AXON_HTTP_PORT = 8024;
private static final int AXON_GRPC_PORT = 8124;
public static void startAxonServer() {
GenericContainer axonServer = new GenericContainer("axoniq/axonserver:latest")
.withExposedPorts(AXON_HTTP_PORT, AXON_GRPC_PORT)
.waitingFor(
Wait.forLogMessage(".*Started AxonServer.*", 1)
);
axonServer.start();
System.setProperty("ENV_AXON_GRPC_PORT", String.valueOf(axonServer.getMappedPort(AXON_GRPC_PORT)));
}
Call startAxonServer method in your #BeforeClass. Now you have to obtain external docker ports (these indicated in withExposedPorts are docker-internal).
You can do it in runtime via getMappedPort as shown in my snippet. Remember to provide connection configuration to your test suite. My example on spring boot as follows:
axon:
axonserver:
servers: localhost:${ENV_AXON_GRPC_PORT}
Complete working solution can be found on my github project.

Retrieve WS request in CXF Web service

I got a CXF OSGi Web service (based on the example demo in servicemix: https://github.com/apache/servicemix/tree/master/examples/cxf/cxf-jaxws-blueprint)
The Web service works fine and i call all the available implemented methods of the service.
My question is how can i retrieve the request inside a WS method and parse in a string XML format.
I have found that this is possible inside interceptors for logging, but i want also to the WS-Request inside my methods.
For storing the request in the database I suggest to extend the new CXF message logging.
You can implement a custom LogEventSender that writes into the database.
I had similar requirement where I need to save data into DB once method is invoked. I had used ThreadLocal with LoggingInInterceptor and LoggingOutInterceptor. For example in LoggingInInterceptor I used to set the message into ThreadContext and in webservice method get the message using LoggingContext.getMessage() and in LoggingOutInterceptor I used to removed the message(NOTE: Need to be careful here you need to explictly remove the message from thread context else you will end up with memory leak, and also incase of client side code interceptors get reversed.
public class LoggingContext {
private static ThreadLocal<String> message;
public static Optional<String> getMessage() {
return Optional.ofNullable(message.get());
}
public static void setMessage(final String message) {
LoggingContext.message = new ThreadLocal<>();
LoggingContext.message.set(message);
}
}
Not an answer to this question but i achieved to do my task by using JAXB in the end and do some manipulations there.

Spring Data Rest Content Type

I am writing unit tests for my application with Spring Data Rest MongoDB. Based on Josh's "Building REST services with Spring" get start guide, I have the following test code:
#Test
public void readSingleAccount() throws Exception {
mockMvc.perform(get("/accounts/"
+ this.account.getId()))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.id", is(this.account.getId())))
.andExpect(jsonPath("$.email", is(this.account.getEmail())))
.andExpect(jsonPath("$.password", is(this.account.getPassword())));
}
And this test fails on the content type.
Content type expected:<application/json;charset=UTF-8> but was: <application/hal+json>
Expected :application/json;charset=UTF-8
Actual :application/hal+json
I don't see MediaType come with HAL. Is the content type defined in another class?
Had the same Problem when not using tomcat (which is configured to return utf-8 using Spring Boot). The solution is to set the accept header in your GET request so the response gets the correct content type:
private MediaType contentType = new MediaType("application", "hal+json", Charset.forName("UTF-8"));
and in your request, do
#Test
public void readSingleAccount() throws Exception {
mockMvc.perform(get("/accounts/"
+ this.account.getId()).**accept(contentType)**)
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.id", is(this.account.getId())))
.andExpect(jsonPath("$.email", is(this.account.getEmail())))
.andExpect(jsonPath("$.password", is(this.account.getPassword())));
}

TDD Best Practice In Using Restful Api in Yii application

I'm constantly looking for the best way to use TDD in Yii app development. Nowday most web app are composed by a fronted, an API layer (usually JSON) to provide async calls to the server and a backend. By my side, most used test in this of app are unit tests and functional ones. The latter the most widely showed in guides and books leverage PHPUnit + Selenium, but Behat + Mink seems very cool too (but I'm not really confident with it yet)
If you ever used functional tests that use a browser (like Selenium) you know that the less you have to run them the better you feel. This cause they're slower, harder to maintain and sometimes (like the popup FB Login using JS SDK) are painful.
When working with a single web page application I care about testing JSON output of my apis. I'd like to test these functionalities with a unit test like approach in order to have faster tests that are easier to maintain. Considering that most of my Controller's action are availaible to Logged only user using accessControl filter I wondered on the best ways to have my tests up and running.
At this moment I think to have two ways to accomplish this
use cUrl toward the desired enpoint to get the JSON directly invoke
the controller's function
In the first scenario I can use fixtures but I got no way to mock CWebUser class (to emulate a logged user), using Apache when the cUrl comes it gets executed by an instance of my CWebApplication that is not the one executed by PHPUnit. I can get rid of this problem by making all my API calls stateless and, as a consequence, removing accessControl filter.
In the second one the only way I found to mock CWebUser class is to override it in the test class that I'm executing. This approach pays until I dont need to test use cases requiring different type of user, and I got no way to change at runtime (or at setup time) my webuser mock. The only way I found to mock my webuser is the one you can find below, this cause $this->getMock('WebUser') doesnt affect anyway CWebApplication's WebUser (read-only) singleton defined in the configuration file.
Here comes a concrete example:
class UserControllerTest extends CDbTestCase
{
public $fixtures=array(
/* NEEDED FIXTURES*/
);
public function testUserCanGetFavouriteStore() {
$controller = new UserController(1);
$result = json_decode($controller->actionAjaxGetFavStore());
$this->assertInternalType('array', $result->data);
$model = $result->data[0];
$this->assertEquals($model->name, "Nome dello Store");
}
}
class WebUser extends CWebUser {
public function getId() {
return 1;
}
public function getIsGuest() {
return false;
}
};
I was wondering if being able to authenticate with the api interface, either by an API key or a user/password combo could be useful.
This is ok if I move toward a almost stateless API integration, but most of the time I just have controller's actions (permitted to logged user only) that returns Json data to populate the frontend.
Anyone can suggest me a better method? Maybe it's just useless to test this kind of JSON output?
Best Regards
Maybe I'm oversimplifying your problem. It sounds like you want to emulate user logins before running the test? If that's the case, why not just create a User object in your fixture and actually log them in before running a test, and log them out after?
Something like:
/**
* Sets up before each test method runs.
* This mainly sets the base URL for the test application.
*/
protected function setUp()
{
parent::setUp();
// login as registered user
$loginForm = new UserLoginForm();
$loginForm->email = USER_EMAIL; // use your fixture
$loginForm->password = USER_PASSWORD; // use your fixture
if(!$loginForm->login()) {
throw new Exception("Could not login in setup");
}
}
protected function tearDown()
{
parent::tearDown();
Yii::app()->user->logout(true);
}
Ok actually the only solution that me and my team found is creating a stub WebUser class.
Rewriting WebUser class in this way you can authenticate a user without having Yii relying on the session.
class WebUserMock extends WebUser {
public function login($identity,$duration=0)
{
$id=$identity->getId();
$states=$identity->getPersistentStates();
if($this->beforeLogin($id,$states,false))
{
$this->changeIdentity($id,$identity->getName(),$states);
$duration = 0;
if($duration>0)
{
if($this->allowAutoLogin)
$this->saveToCookie($duration);
else
throw new CException(Yii::t('yii','{class}.allowAutoLogin must be set true in order to use cookie-based authentication.',
array('{class}'=>get_class($this))));
}
$this->afterLogin(false);
}
return !$this->getIsGuest();
}
public function changeIdentity($id,$name,$states)
{
$this->setId($id);
$this->setName($name);
$this->loadIdentityStates($states);
}
// Load user model.
protected function loadUser() {
$id = Yii::app()->user->id;
if ($id!==null)
$this->_model=User::model()->findByPk($id);
return $this->_model;
}
};
In the setUp method of your test class you can login any user (in this case leveraging my fixtures)
//a piece of your setUp() method....
$identity = new UserIdentity($this->users('User_2')->email, md5('demo'));
$identity->authenticate();
if($identity->errorCode===UserIdentity::ERROR_NONE)
Yii::app()->user->login($identity);
As a final thing to do just override the user component in the test configuration file and tell him to use this one:
protected/config/test.php
'user'=>array(
'class' => 'application.tests.mock.WebUserMock',
'allowAutoLogin'=>false,
),
Still not sure that this is the best way to handle it but seems to work fine

How do you set a custom session when unit testing with wicket?

I'm trying to run some unit tests on a wicket page that only allows access after you've logged in. In my JUnit test I cannot start the page or render it without setting the session.
How do you set the session? I'm having problems finding any documentation on how to do this.
WicketTester tester = new WicketTester(new MyApp());
((MyCustomSession)tester.getWicketSession()).setItem(MyFactory.getItem("abc"));
//Fails to start below, no session seems to be set
tester.startPage(General.class);
tester.assertRenderedPage(General.class);
What I frequently do is to provide a fake WebApplication with overrides for things that I want to mock or stub.
Among the things I override is the method
public abstract Session newSession(Request request, Response response);
which allows you to return a fake session setup with anything you want.
This is in Wicket 1.3 - if you're using 1.4, some of this may have changed, and as noted in another response, it may be related to a wicket bug.
But assuming the interface hasn't changed too much, overriding this method may also be another way of working around the issue in WICKET-1215.
You may be running into WICKET-1215. Otherwise what you're doing looks fine. For example, I have a Junit4 setup method that looks like:
#Before
public void createTester() {
tester = new WicketTester( new MyApp() );
// see http://issues.apache.org/jira/browse/WICKET-1215
tester.setupRequestAndResponse();
MyAppSession session = (MyAppSession) tester.getWicketSession();
session.setLocale(Locale.CANADA);
session.setUser(...);
}
Using Wicket 1.4, I use my normal WebApplication and WebSession implementations, called NewtEditor and NewtSession in my app. I override newSession, where I do the same than in the regular app code, except that I sign in right away. I also override newSessionStore for performance reasons, I copied this trick from WicketTesters code.
tester = new WicketTester(new NewtEditor()
{
#Override
public Session newSession(Request request, Response response)
{
NewtSession session = new NewtSession(request);
session.signIn(getTestDao());
return session;
}
#Override
protected ISessionStore newSessionStore()
{
// Copied from WicketTester: Don't use a filestore, or we spawn lots of threads,
// which makes things slow.
return new HttpSessionStore(this);
}
});