# Migrating from Developing Asynchronous Multi-Threading Programs to Reactive Programs in Java
## 7 Evaluation of Results
### Functional Equivalence  
An inherent part of the migration process involved ensuring that both implementations produced identical results when performing the same operations. This consistency goes beyond preserving existing functionality but also ensures accurate performance analysis, as comparing performance indicators would be unreliable due to functional differences. Achieving functional equivalence required a robust testing methodology, featuring tests at different levels of abstraction and ensuring correct execution. Some tests validate individual components, ensuring smaller code units perform as expected, while others evaluate overall system behavior, emphasizing interactions and integrations by simulating real user actions. This approach guarantees that both individual components and the system as a whole are properly evaluated, preserving functionality throughout the transition to the reactive model. Unit tests, created using JUnit 5 [62], were essential to check the accuracy and consistency of individual units of code. Aiming to be as similar as possible in both implementations, these tests were tailored only to account for differences such as data types and methods specific to each paradigm. Each test is structured to prepare the conditions and prerequisites, execute the tested functionality, and verify the expected results, thereby ensuring reliable testing of individual components throughout the migration process. Listing 4 illustrates an example of a unit test for the user login functionality in the asynchronous approach. ```
@Test voidtestLoginSuccess() throwsExecutionException, InterruptedException{ //arrange UserLoginRequest request = newUserLoginRequest( \"[EMAIL_ADDRESS]\", \"correctpassword\" ); UserEntity userEntity = newUserEntity(); userEntity.setEmail(\"[EMAIL_ADDRESS]\"); userEntity.setPassword(\"$2$10$SomeHashedPasswordHere\"); userEntity.setId(UUID.randomUUID()); when(userRepository.findByEmail(any()) ).thenReturn(Optional.of(userEntity)); when encoder.matches(request.getPassword(),userEntity.getPassword()) ).thenReturn(true); //act AuthenticationResponse result = authenticationService.login(request).get(); //assertNotNull(result); assertFalse(result.getToken().isEmpty()); verify(userRepository).findByEmail(request.getEmail()); verify(encoder).matches(request.getPassword(),userEntity.getPassword()); }
```  
Listing 4: Example of unit test covering the user login in the asynchronous approach. The introduced test example validates the user login functionality by setting up a login request with valid credentials and a mocked user entity. It configures the repository and the encoder mocks to return the expected values, simulating real-world conditions. Following this setup, the test calls the actual login method on the authentication service, concluding with response checks to ensure a successful login. Assertions ensure that the response is not null, that the token is present, and that the correct methods were invoked with the expected parameters, confirming the login process's accuracy and reliability. Cucumber [63] was also employed to define Behavior-Driven Development (BDD) automated tests. Written in Gherkin, these tests describe software behavior in clear and understandable terms, remaining agnostic of implementation details so they can be used across both versions of the application. These testing scenarios focus on assessing functional consistency by outlining the expected system behavior from a user's perspective, described in a human-readable language. The abstraction from the code streamlines the validation of overall system functionality and integration points, ensuring a consistent user experience while also serving as a helpful instrument in identifying and addressing any discrepancies during the development and migration process. Listing 5 illustrates a partial Cucumber feature file for message management.