I created some integration tests using wiremock. They pass individually, but when I execute the test task from gradle they fail with the following error:
2022-05-09 11:42:40.077 ERROR 23116 --- [tp1400751713-44] WireMock : No response could be served as there are no stub mappings in this WireMock instance.
The special thing about this, it is a Spring ApplicationRunner, so the wiremock has to be ready and stubbed before the test class is initialized
Here is one of my test classes, the other is very similar:
#ActiveProfiles({"test"})
#SpringBootTest(args = {"src/test/resources/data/productTestData.xml", "--operation=createProduct"})
#ExtendWith(OutputCaptureExtension.class)
public class CreateProductsFunctionalTest {
private static final String PRODUCT_CREATE_REQUEST_PATH = "product_create_request.json";
private static final String PRODUCT2_CREATE_REQUEST_PATH = "product2_create_request.json";
private static final String PRODUCT_CREATE_RESPONSE_PATH = "product_create_response.json";
private static final String PRODUCT2_CREATE_RESPONSE_PATH = "product2_create_response.json";
private static final int FLUENT_PORT = 8085;
#RegisterExtension
private static final WireMockExtension FLUENT_WM =
WireMockExtension.newInstance().options(wireMockConfig().port(FLUENT_PORT)).build();
#BeforeAll
static void setup() {
fluentMockAuthentication(FLUENT_WM, OAUTH_SUCCESS_RESPONSE_PATH, OK.value());
fluentMockGraphQL(FLUENT_WM, PRODUCT_CREATE_REQUEST_PATH, PRODUCT_CREATE_RESPONSE_PATH);
fluentMockGraphQL(FLUENT_WM, PRODUCT2_CREATE_REQUEST_PATH, PRODUCT2_CREATE_RESPONSE_PATH);
}
#DisplayName("when creating product information")
#Test
void shouldInvokeCreateProducts(final CapturedOutput output) {
assertThat(output).contains("Product with ref '00001111' successfully created");
assertThat(output).contains("Product with ref '00002222' successfully created");
}
}
The stubbing happens in the fluentMockAuthentication and fluentMockGraphQL methods.
Related
I am yet to find a solution to this.
My Test class:
#WebFluxTest(controllers = {PatronController.class})
#Import({PatronService.class}) //to satisfy PatronController dependency.
#ExtendWith({PatronParameterResolver.class})
class PatronFunctionsSpec {
private Patron patron;
private Mono<Patron> patronMono;
private final PatronService patronService = Mockito.mock(PatronService.class);
#MockBean
private PatronRepository patronRepository;
#Autowired
private WebTestClient client;
#BeforeEach
void init(Patron injectedPatron) {
patron = injectedPatron;
patronMono = Mono.just(patron);
}
//Patron Story: patron wants to create an account with us
#Nested
#DisplayName("Creating a patron.")
class CreatingPatron {
#Test
#DisplayName("PatronService.create() returns success msg in Response obj after creating patron.")
void getResponseObjFromServiceCreate() {
Flux<Patron> patronFlux = Flux.from(patronMono);
Mockito.when(patronRepository.saveAll(patronMono)).thenReturn(patronFlux);
PatronService patronService = new PatronService(patronRepository);
Mono<Response> responseStream = patronService.create(Mono.just(patron));
Mono<Response> expectedResponseStream = Mono.just(new Response("Welcome, patron. Can't show emojis yet -- sorry."));
assertEquals(expectedResponseStream, responseStream);
}
}
}
See my PatronService class with its code:
#Service
public class PatronService {
private final PatronRepository patronRepository;
public PatronService(PatronRepository patronRepository) {
this.patronRepository = patronRepository;
}
/**
*
* persists patron via PatronRepo
*/
public Mono<Response> create(Mono<Patron> patronMono) {
patronRepository.saveAll(patronMono).subscribe();
return Mono.just(new Response("Welcome, patron. Can't find the emojis yet -- sorry."));
}
}
I am testing the PatronService's create(), so need to mock and stub PatronRepository and its function respectively. But the problem is: after running the test case, I get this exception:
java.lang.NullPointerException: Cannot invoke "reactor.core.publisher.Flux.subscribe()" because "patronFlux" is null
at com.budgit.service.PatronService.create(PatronService.java:26)
How can I fix this?
I'm trying to do a simple test class using MockMvc. But I'm stuck in a really simple thing (but the docs doesn't help).
My basic code is
#SpringBootTest
#AutoConfigureMockMvc
class RecommendationServiceApplicationTests {
private static final Logger LOG = LoggerFactory.getLogger(RecommendationServiceApplicationTests.class);
private final String url = "/recommendation?productId=%d";
static final int PRODUCT_OK = 1;
static final int PRODUCT_KO = 0;
static final int PRODUCT_NOT_FOUND = 113;
#Autowired
private MockMvc mockMvc;
// Check OK response
#Test
public void getRecommendationsOK() throws Exception {
MockHttpServletRequestBuilder requestBuilder;
MvcResult result;
String contentResponse;
Recommendation[] recommendations;
requestBuilder=
MockMvcRequestBuilders
.get("/recommendation?productId=1")
.accept(MediaType.APPLICATION_JSON);
result = this.mockMvc
.perform(requestBuilder)
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andReturn();
}
}
In this way, the code runs fine, but when I try to use a parameterized query, I cannot find the way
I have tried (with no success)
.get("/recommendation?productId=",PRODUCT_OK)
.get("/recommendation?productId={}",PRODUCT_OK)
.get("/recommendation?productId=%d",PRODUCT_OK)
.get("/recommendation?productId=[]",PRODUCT_OK)
.get("/recommendation?productId=PRODUCT_OK",PRODUCT_OK)
Thanks in advance
Apart from .param(), you can also use the .get(String urlTemplate, Object... uriVars) that you already tried. You were pretty close to the solution:
.get("/recommendation?productId={product}",PRODUCT_OK)
You can also expand multiple URI variables
.get("/recommendation?productId={product}&sort={var}",PRODUCT_OK, "desc");
Use .param()
MockMvcRequestBuilders
.get("/recommendation")
.param("product", "1")
.accept(MediaType.APPLICATION_JSON);
I'm writing some test classes for my services and noticed a weird behavior that happens after test results are returned. The console prints 'null' messages and no other information about tests. The tests work fine as I've tried to fail them to ensure that is the case. Is this the expected behavior?
#RunWith(MockitoJUnitRunner.class)
public class GradeServiceTest {
#Rule
public ExpectedException thrown = ExpectedException.none();
#Mock
private GradeRepository gradeRepository;
#Mock
private AssignmentService assignmentService;
#InjectMocks
private GradeService gradeService;
private Assignment assignment;
private Grade grade;
#Before
public void init() {
assignment = new Assignment.Builder()
.withId(0L)
.withModuleId(0L)
.withName("Test assignment")
.withWeightEnabled(false)
.withWeight(0)
.build();
grade = new Grade.Builder()
.withId(0L)
.withAssignmentId(0L)
.withGradeType(GradeType.PERCENTAGE)
.withGradeValue("50%")
.build();
}
#Test
public void shouldAddGrade() throws AssignmentException, GradeException {
// GIVEN
when(assignmentService.exists(grade.getAssignmentId())).thenReturn(true);
when(gradeRepository.insert(any())).thenReturn(grade);
// WHEN
Grade addedGrade = gradeService.addGrade(grade.getAssignmentId(), grade.getType().name(), grade.getValue());
// THEN
assertThat(addedGrade).isEqualTo(grade);
}
#Test
public void shouldNotAddGradeIfAssignmentDoesNotExist() throws AssignmentException, GradeException {
// GIVEN
when(assignmentService.exists(grade.getAssignmentId())).thenReturn(false);
// EXPECT
thrown.expect(AssignmentException.class);
thrown.expectMessage(AssignmentErrorMessages.NOT_FOUND);
// THEN
gradeService.addGrade(grade.getAssignmentId(), grade.getType().name(), grade.getValue());
}
}
I don't think this is normal behavior for each test to be printing 'null' without any other information. Could someone help me understand what is wrong with the code?
Test results:
null
null
Process finished with exit code 0
Summary & first problem
I am trying to test my user registration mechanism. When a new user account is created via my REST API, a UserAccountCreatedEvent is stored in the database. A scheduled task checks the database every 5 seconds for new UserAccountCreatedEvents and if one is present, sends an email to the registered user. When running my tests I encounter the problem that the table for the UserAccountCreatedEvent can't be found (see exception below). I used to send the email in a blocking manner in the service method, but I recently switched to this async approach. All my tests worked perfectly for the blocking approach and the only thing I changed for the async approach is to include Awaitility in the test.
2019-04-23 11:24:51.605 ERROR 7968 --- [taskScheduler-1] o.s.s.s.TaskUtils$LoggingErrorHandler : Unexpected error occurred in scheduled task.
org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [select useraccoun0_.id as id1_0_, useraccoun0_.completed_at as complete2_0_, useraccoun0_.created_at as created_3_0_, useraccoun0_.in_process_since as in_proce4_0_, useraccoun0_.status as status5_0_, useraccoun0_.user_id as user_id1_35_ from user_account_created_event useraccoun0_ where useraccoun0_.status=? order by useraccoun0_.created_at asc limit ?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException:
Table "USER_ACCOUNT_CREATED_EVENT" not found; SQL statement:
select useraccoun0_.id as id1_0_, useraccoun0_.completed_at as complete2_0_, useraccoun0_.created_at as created_3_0_, useraccoun0_.in_process_since as in_proce4_0_, useraccoun0_.status as status5_0_, useraccoun0_.user_id as user_id1_35_ from user_account_created_event useraccoun0_ where useraccoun0_.status=? order by useraccoun0_.created_at asc limit ? [42102-199]
Full stack trace
Second problem
As if that were not enough, the tests behave completely different when running them in debug mode. When I set a breakpoint in the method that is called by the method which is annotated with #Scheduled, it is invoked several times althogh #Scheduled is configured with a fixedDelayString (fixed delay) of 5000ms. Thanks to logging I can even see that several mails were sent. Still, my test SMTP sever (GreenMail) does not receive any emails. How is this even possible? I've intentionally set the transaction isolation to Isolation.SERIALIZABLE so that it should be impossible (as far as I understand transaction isolation) that two scheduled methods access the same Event from the database.
Third problem
To cap it all, when I rerun the failed tests, THEY WORK. But, there are different exceptions on the console (see below). But still, the app starts and the tests finish successfully. There are different test results depending on if I run all tests vs. only the class vs. only the method vs. rerun failed tests. I don't understand how such an indeterministic behaviour can be possible.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: Failed to scan classpath for unlisted entity classes
Caused by: java.nio.channels.ClosedByInterruptException: null
Full stack trace
My code
Test class (UserRegistrationTest)
#ActiveProfiles("test")
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
#SpringBootTest
#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
public class UserRegistrationTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper objectMapper;
#Autowired
private Routes routes;
#Autowired
private TestConfig testConfig;
#Resource(name = "validCustomerDTO")
private CustomerDTO validCustomerDTO;
#Resource(name = "validVendorDTO")
private VendorRegistrationDTO validVendorRegistrationDTO;
#Value("${schedule.sendRegistrationConfirmationEmailTaskDelay}")
private Short registrationConfirmationEmailSenderTaskDelay;
private GreenMail smtpServer;
// Setup & tear down
#Before
public void setUp() {
smtpServer = testConfig.getMailServer();
smtpServer.start();
}
#After
public void tearDown() {
smtpServer.stop();
}
// Tests
#Test
public void testCreateCustomerAccount() throws Exception {
mockMvc.perform(
post(routes.getCustomerPath())
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(objectMapper.writeValueAsString(validCustomerDTO)))
.andExpect(status().isCreated());
// When run normally, I get a timeout from the next line
await().atMost(registrationConfirmationEmailSenderTaskDelay + 10000, MILLISECONDS).until(smtpServerReceivedOneEmail());
// Verify correct registration confirmation email was sent
MimeMessage[] receivedMessages = smtpServer.getReceivedMessages();
assertThat(receivedMessages).hasSize(1);
// other checks
// ...
}
#Test
public void testCreateVendorAccount() throws Exception {
mockMvc.perform(
post(routes.getVendorPath())
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(objectMapper.writeValueAsString(validVendorRegistrationDTO)))
.andExpect(status().isCreated());
// When run normally, I get a timeout from the next line
await().atMost(registrationConfirmationEmailSenderTaskDelay + 10000, MILLISECONDS).until(smtpServerReceivedOneEmail());
// Verify correct registration confirmation email was sent
MimeMessage[] receivedMessages = smtpServer.getReceivedMessages();
assertThat(receivedMessages).hasSize(1);
// other checks
// ...
}
// Helper methods
private Callable<Boolean> smtpServerReceivedOneEmail() {
return () -> smtpServer.getReceivedMessages().length == 1;
}
// Test configuration
#TestConfiguration
static class TestConfig {
private static final int PORT = 3025;
private static final String HOST = "localhost";
private static final String PROTOCOL = "smtp";
GreenMail getMailServer() {
return new GreenMail(new ServerSetup(PORT, HOST, PROTOCOL));
}
#Bean
public JavaMailSender javaMailSender() {
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
javaMailSender.setHost(HOST);
javaMailSender.setPort(PORT);
javaMailSender.setProtocol(PROTOCOL);
javaMailSender.setDefaultEncoding("UTF-8");
return javaMailSender;
}
}
Task scheduler (BusinessTaskScheduler)
#Component
public class BusinessTaskScheduler {
private final RegistrationTask registrationTask;
#Autowired
public BusinessTaskScheduler(RegistrationTask registrationTask) {
this.registrationTask = registrationTask;
}
#Scheduled(fixedDelayString = "${schedule.sendRegistrationConfirmationEmailTaskDelay}")
public void sendRegistrationConfirmationEmail() {
registrationTask.sendRegistrationConfirmationEmail();
}
}
The code that is called by the scheduled method (RegistrationTask)
#Component
#Transactional(isolation = Isolation.SERIALIZABLE)
public class RegistrationTask {
private final EmailHelper emailHelper;
private final EventService eventService;
private final UserRegistrationService userRegistrationService;
#Autowired
public RegistrationTask(EmailHelper emailHelper, EventService eventService, UserRegistrationService userRegistrationService) {
this.emailHelper = emailHelper;
this.eventService = eventService;
this.userRegistrationService = userRegistrationService;
}
public void sendRegistrationConfirmationEmail() {
Optional<UserAccountCreatedEvent> optionalEvent = eventService.getOldestUncompletedUserAccountCreatedEvent();
if (optionalEvent.isPresent()) {
UserAccountCreatedEvent event = optionalEvent.get();
User user = event.getUser();
RegistrationVerificationToken token = userRegistrationService.createRegistrationVerificationTokenForUser(user);
emailHelper.sendRegistrationConfirmationEmail(token);
eventService.completeEvent(event);
}
}
}
The event service (EventServiceImpl)
#Service
#Transactional(isolation = Isolation.SERIALIZABLE)
public class EventServiceImpl implements EventService {
private final ApplicationEventDAO applicationEventDAO;
private final UserAccountCreatedEventDAO userAccountCreatedEventDAO;
#Autowired
public EventServiceImpl(ApplicationEventDAO applicationEventDAO, UserAccountCreatedEventDAO userAccountCreatedEventDAO) {
this.applicationEventDAO = applicationEventDAO;
this.userAccountCreatedEventDAO = userAccountCreatedEventDAO;
}
#Override
public void completeEvent(ApplicationEvent event) {
if (!event.getStatus().equals(COMPLETED) && Objects.isNull(event.getCompletedAt())) {
event.setStatus(COMPLETED);
event.setCompletedAt(LocalDateTime.now());
applicationEventDAO.save(event);
}
}
#Override
public Optional<UserAccountCreatedEvent> getOldestUncompletedUserAccountCreatedEvent() {
Optional<UserAccountCreatedEvent> optionalEvent = userAccountCreatedEventDAO.findFirstByStatusOrderByCreatedAtAsc(NEW);
if (optionalEvent.isPresent()) {
UserAccountCreatedEvent event = optionalEvent.get();
setEventInProcess(event);
return Optional.of(userAccountCreatedEventDAO.save(event));
}
return Optional.empty();
}
#Override
public void publishEvent(ApplicationEvent event) {
applicationEventDAO.save(event);
}
// Helper methods
private void setEventInProcess(ApplicationEvent event) {
event.setStatus(Status.IN_PROCESS);
event.setInProcessSince(LocalDateTime.now());
}
}
The UserAccountCreatedEvent
application.yml
schedule:
sendRegistrationConfirmationEmailTaskDelay: 5000 # delay between tasks in milliseconds
I am new to scheduling with Spring, so any help is greatly appreciated!
I'm trying to set up JunitTest using camel, activeMq and an Alfresco API
The route I want to test is :
from(Constantes.Direct.DIRECT_GET_AUTHENTIFICATION_TICKET)
.setBody().simple("{"
+ "\"userId\": \"userId\","
+"\"password\": \"password\""
+"}")
.setHeader(Exchange.HTTP_METHOD,constant(Constantes.Headers.HTTP_POST))
.setHeader(Exchange.HTTP_URI,simple(Constantes.Urls.OBTENIR_TICKET))
.to(Constantes.Urls.DUMMYHOST).convertBodyTo(String.class)
.unmarshal().json(JsonLibrary.Jackson, TicketAlfresco.class).process(new Dumper())
.process(new TokenBase64Proc())
.setHeader(Constantes.Headers.SENDER, constant(Constantes.Headers.ALFRESCO))
.setHeader(Constantes.Headers.API_ACTION, constant(SET_ALFRESCO_TOKEN))
.setHeader(Constantes.Headers.HEADER_AUTHORIZATION, simple("${body}"))
.inOut(Constantes.ActiveMq.ACTIVEMQ_IN)
.end();
The first "to" send a request to the Alfresco API and give back a new token.
The last inOut send the token to an activeMQ.
The problem is that when I want to test my route, when the test arrive to inOut inside the activeMq, the test fail because it didn't get any answer.
Do I need to install and embeded broker activeMQ or do I need to Mock the ActiveMQ ? And how can I do that?
For the moment to make it run I use :
mockEndpointsAndSkip("activemq:IN")
But I'm not sure that is the good solution.
Here is the test I have for the moment:
#RunWith(SpringRunner.class)
#EnableAutoConfiguration
#ComponentScan(basePackages = {"fr.gif.wsp.web.service.alfresco*"})
public class RouteGetAuthentificationTicketTest extends CamelTestSupport{
#Autowired private RouteGetAuthentificationTicket routeGetAuthentificationTicket;
//Route to test
private final static String FOURNISSEUR_GET_AUTHENTIFICATION_TICKET = Constantes.Direct.DIRECT_GET_AUTHENTIFICATION_TICKET;
private final static String MOCK_FOURNISSEUR_GET_AUTHENTIFICATION_TICKET = "mock:" + FOURNISSEUR_GET_AUTHENTIFICATION_TICKET;
// Mock result
private final static String MOCK_RESULT = "mock:result";
//Data
private final static String BODY = "Content of the body";
#Override
protected RoutesBuilder createRouteBuilder() {
return routeGetAuthentificationTicket;
}
#Before
public void setContextRoute() throws Exception {
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
mockEndpointsAndSkip("activemq:IN");
weaveAddLast().to(MOCK_RESULT);
}
});
}
#Test
public void getAuthentificationTicket() throws InterruptedException {
final MockEndpoint resultEndpoint = context.getEndpoint(MOCK_FOURNISSEUR_GET_AUTHENTIFICATION_TICKET, MockEndpoint.class);
context.createProducerTemplate().sendBody(FOURNISSEUR_GET_AUTHENTIFICATION_TICKET, BODY);
resultEndpoint.assertIsSatisfied();
final Object result = context.createProducerTemplate().requestBody(FOURNISSEUR_GET_AUTHENTIFICATION_TICKET, BODY);
assertNotNull(result);
}}
Thanks for your time