I am able to generate restdocs for rest services which is created by me, but unable to generate docs for services which i am consuming.
Is there any way to test and generate docs for third party API.
Sample code which i am using to generate to docs for local services.
#RunWith(SpringRunner.class)
#WebAppConfiguration
#SpringBootTest(classes = RestdocApplication.class)
public class CountryDocumentation {
private static final Logger logger =
LoggerFactory.getLogger(CountryDocumentation.class);
private MockMvc mockMvc;
#Autowired
private WebApplicationContext context;
#Rule
public final JUnitRestDocumentation restDocumentation = new
JUnitRestDocumentation("target/generated-snippets");
#Mock
private CountryService countryService;
#Mock
private RestTemplate restTemplate;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).apply(documentationConfiguration(this.restDocumentation)
.uris().withHost("X.X.X.X").withPort(9090).and().operationPreprocessors()
.withResponseDefaults(prettyPrint())
.withRequestDefaults(prettyPrint())).defaultRequest(get("/")).build();
}
#Test
public void getCountryDefinition() throws Exception {
this.mockMvc.perform(get("/"))
.andExpect(status().is(200))
.andDo(document("{ClassName}/{methodName}"));
}
}
You've said in a comment that you want to mock the actual call to the remote service. I would argue that makes the documentation pointless. If your tests that generate the documentation are calling a mocked service, you're documenting the mock not the service. If you want the benefits of REST Docs' test-driven approach to documentation generation, your tests need to call the service that is being documented. If the service is only remotely accessible then you'll need to make HTTP calls to document it.
You can use Spring REST Docs with REST Assured or WebTestClient to document any service that's accessible via HTTP. Here's an example with REST Assured that documents part of Stack Exchange's API:
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.restdocs.JUnitRestDocumentation;
import static io.restassured.RestAssured.given;
import static org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.document;
import static org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.documentationConfiguration;
public class RestAssuredExampleTests {
#Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
private RequestSpecification documentationSpec;
#Before
public void setUp() {
this.documentationSpec = new RequestSpecBuilder()
.addFilter(documentationConfiguration(this.restDocumentation))
.setBaseUri("https://api.stackexchange.com/2.2").build();
}
#Test
public void answers() throws Exception {
given(this.documentationSpec).accept(ContentType.JSON).filter(document("answers"))
.when().get("answers?order=desc&sort=activity&site=stackoverflow").then()
.assertThat().statusCode(200);
}
}
There are many products for mocking/virtualizing services. Including SoapUI and Parasoft Virtualize.
Related
I'm running a basic Spring App with Mockito 3.1.0 and and Junit 5.5.2. I have a Service call that includes two Spring Data JPA repositories. These are passed into the constructor for DI (along with two others that are immaterial – I'm including them in case they could also, potentially, cause errors.) I see no issues with the service when the app runs.
When I run my test, I get a NPE for myService. Stepping through the stack trace, hasn't really shown me anything that relates to the error. I have also tried (following this Article: https://www.baeldung.com/mockito-junit-5-extension) updating my test class to look like this:
#ExtendWith(MockitoExtension.class)
#RunWith(JUnitPlatform.class) // This dependency doesn't seem to exist
public class MyServiceTest {
// ...
#BeforeEach
// not the JUnit4 #Before annotation.
// Interestingly, this gives me NPEs for the repositories, not the service.
public void setup(){
// ...
}
}
to no avail. What I suspect is happening is that something about my setup isn't properly wired up – either as dependencies or syntax for DI.
How do I debug this? What am I missing? Thanks in advance!
Service:
import org.springframework.stereotype.Service;
#Service
public class MyService {
private final Repository1 repository1;
private final Repository2 repository2;
private final Repository3 repository3;
private final Repository4 repository4;
public MyService(Repository1 repository1,
Repository2 repository2,
Repository3 repository3,
Repository4 repository4) {
this.repository1 = repository1;
this.repository2 = repository2;
this.repository3 = repository3;
this.repository4 = repository4;
}
public Boolean computeValue(String someInput) {
// does computations with repository1, repository2.
}
}
Test:
import static org.mockito.Mockito.when;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
#Mock
private Repository1 repository1;
#Mock
private Repository2 repository2;
#Mock
private Repository3 repository3;
#Mock
private Repository4 repository4;
#InjectMocks
private MyService myService;
#Before
public void setup {
when(repository1.findAll()).thenReturn(new ArrayList<>());
when(repository1.findAllByInput(anyString())).thenReturn(new ArrayList<>());
// Yes; I'm aware that this could also be a call to
// MockitoAnnotations.initMocks(this). I've tried it:
// it doesn't work. Also, I've intentionally not taken this
// approach due to reasons:
// - https://stackoverflow.com/questions/10806345/runwithmockitojunitrunner-class-vs-mockitoannotations-initmocksthis
}
#Test
void callMyService() {
assertTrue(myService.computeValue("123"));
}
}
Sample Repository:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
// This is just an example, but this pattern is repeated against all
// repositories in the project.
#Repository
public interface Repository1 extends JpaRepository<Repository1, String> {
}
Edit:
I forgot to mention that I have other files in this project that are using exactly these annotations (#RunWith(MockitoJUnitRunner.class), #Mock, #InjectMocks, #Before) that are not failing.
I updated the files with the relevant imports, and added an example of RepositoryN.
I update the MyService class to better reflect the parameters.
For anybody else who encounters this in the future, we were able to fix this problem by changing one of the imports from:
import org.junit.jupiter.api.Test;
to
import org.junit.Test;
Edit:
This had to do with differing versions of JUnit. There's a good long-form explanation as to why here.
Seems like your object myService, is not instantiated. I would suggest not use #InjectMocks and directly create your object as your repositories are already instantiated.
MyService myService = new MyService(..., ..., ...)
I suppose you have to annotate your test class with #ExtendWith(SpringExtension.class) and not with MockitoExtension.class
More info here Junit 5 with Spring Boot: When to use #ExtendWith Spring or Mockito?
I have tried to run the below test and am facing NotAMock Exception and not sure how to resolve it. I have been trying to read the concept that methods of class under test cannot be mocked but I am unable to come clear on the subject. If someone could explain me the Why on my own example, I am hopeful of understanding it better.
I tried various ways of changing #RunWith runners for Unit or Integration test setup or using #Spy instead of #Mock or not have #Autowired etc but either was facing dao Null Pointer or Not a Mock Exception variably.
Am I supposed to Use another class and inject the Listener in that class and mock the listener to achieve the functionality of being able to mock the methods and capture the arguments dynamically. Will this work because it is no more the class under test and therefore the methods could be mocked? If so, how is this realized. If not, what is the right way. My sense is moving the listener to another class will only extend my current set of issues of not being able to mock but does not resolve it. However, I am not sure what is the right outcome.
#Component
public class FileEventListener implements ApplicationListener<FileEvent> {
#Autowired private FetchFileDetailsDAO fileDao;//Dao is annotated with #Transactional
#Override
public void onApplicationEvent(FileEvent event) {
fileDao.getDetailsForFile(event.fileName())
}
}
-----------------------------------------------------------------------------------------
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
#SpringBootTest(classes = TestApp.class)
#RunWith(SpringRunner.class)
public class TestClass {
#Captor private ArgumentCaptor<Object> captor;
#Mock #Autowired private FetchFileDetailsDAO dao;
#InjectMocks #Autowired private FileEventListener listener;
#Before
public void setup() throws IOException {
MockitoAnnotations.initMocks(this);
}
#Test
#Transactional
#Rollback(true)
public void test() throws Exception {
FileEvent ev = new FileEvent();
...
listener.onApplicationEvent(ev);
verify(dao, times(1)).getDetailsForFile((String)captor.capture())
}
You are mixing things up here. There is an important difference between #Mock and #MockBean.
You use the first annotation if you want to write a unit test without any Spring Context support (speak #SpringBootTest, #DataJpaTest, etc.). For such tests, you can use #Mock and #InjectMocks.
As you are writing an integration test (you are starting the whole context with #SpringBootTest), you work with managed Spring beans inside your test. Hence you are not writing a unit test anymore.
If you want to replace a Spring bean with a mocked version of it inside your Spring Test Context, you have to use #MockBean:
#SpringBootTest(classes = TestApp.class)
#RunWith(SpringRunner.class)
#RunWith(MockitoJUnitRunner.class) // will do the Captor initialization for you
public class TestClass {
#Captor
private ArgumentCaptor<Object> captor;
#MockBean
private FetchFileDetailsDAO dao;
#Autowired
private FileEventListener listener;
#Test
#Transactional
#Rollback(true)
public void test() throws Exception {
FileEvent ev = new FileEvent();
// ...
listener.onApplicationEvent(ev);
verify(dao, times(1)).getDetailsForFile((String)captor.capture())
}
Starting the whole context however for this test is IMHO overkill. You are better off writing a good old unit test with just JUnit and Mockito.
In addition to this, also I would rethink what benefit your current tests adds to your project as it is literally duplicating the business logic. Maybe there is more code that is not present here.
You can find a more detailed summary for the difference between #Mock and #MockBean in this article.
I think the problem is the following line
#Mock #Autowired private FetchFileDetailsDAOImpl dao;
Try #Mock private FetchFileDetailsDAOImpl dao; instead
I am trying to test a service that has a mongodb repository. I dont know how to write tests that utilise it though and when I try to create entries from the testing class I keep on getting null pointer exception when I call a method from the mongodb repository. My repository class is called
TagPreferencesRepository
and the exception is thrown when I call
tagPreferencesRepository.deleteAllByTag(tag);
I have declared TagPreferencesRepository using #Mock:
#Mock TagPreferencesRepository tagPreferencesRepository;
and the method throwing the exception is called from inside a method in the testing class, that is annotated with #Before
Methods in the testing class annotated with #Before:
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
and:
#Before
public TagPreferences setUp() {
tagPreferencesRepository.deleteAllByTag(tagEnum);
...
}
Your mock is not being correctly initialized.
It could be that you are not injecting the mock to the instance of what you want to test. Do you have a #InjectMock annotation?
Also, make sure that you are using jUnit for your annotations. I once had a similar experience because I was using org.junit.jupiter.api for some of the annotations instead of org.junit.
Example skeleton:
import org.junit.Before;
import org.junit.Test
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
public class TestSomeService()
{
#InjectMock
SomeService someService;
#Mock
TagPreferencesRepository tagPreferencesRepository;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
#Test
public void SomeTest(){
TagEnum tag = SomeTag;
// if deleteAllByTag is void
doNothing().when(tagPreferencesRepository).deleteAllByTag(tag);
someResult = someService.someMethod(tag);
// assert something
}
}
In my Spring Boot test I'm using 2 mock beans with different qualifiers:
#RunWith(SpringRunner.class)
#SpringBootTest
class HohoTest {
#MockBean #Qualifier("haha") IHaha ahaha;
#MockBean #Qualifier("hoho") IHaha ohoho;
}
Since I'm not using these beans explicitly, I would rather move them away from the class body, as the #MockBean annotation is now repeatable:
#RunWith(SpringRunner.class)
#SpringBootTest
#MockBean(IHaha.class)
#MockBean(IHaha.class)
class HohoTest {}
However, I need to pass in a qualifier as well, since they have the same type. Any idea on how I can achieve that?
Because using annotation #Qualifier means choose bean by name, so you can set up a name for a mock with code like this:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {JsonMapperConfig.class})
public class IntegrationFlowTest {
#MockBean(name = "s3MessageRepository")
private S3Repository s3MessageRepository;
// etc
If it is okay to move the mock definition completely out of the test class, you could also create the mocks in a separate #Configuration class:
#Configuration
public class MockConfiguration
{
#Bean #Qualifier("haha")
public IHaha ahaha() {
return Mockito.mock(IHaha.class);
}
#Bean #Qualifier("hoho")
public IHaha ohoho() {
return Mockito.mock(IHaha.class);
}
}
When declaring #MockBean at the class level, there is currently no support for providing a qualifier.
If you would like to have such support, I suggest you request it in the Spring Boot issue tracker.
Otherwise, you will need to continue declaring #MockBean on fields alongside #Qualifier.
I had a similar requirement of injecting mocked service beans with #Order annotation. I also needed to verify the invocation count of service functions. Below is my implementation. It might help someone.
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest
public class ServiceNameTest {
#Autowired private ServiceName serviceName;
// Important: Used to reset interaction count of our static
// bean objects before every test.
#Before
public void reset_mockito_interactions() {
Mockito.clearInvocations(MockServicesConfig.bean1);
Mockito.clearInvocations(MockServicesConfig.bean2);
}
#Configuration
public static class MockServicesConfig {
public static InterfaceName bean1;
public static InterfaceName bean2;
#Bean
#Order(1)
public InterfaceName bean1() {
bean1 = Mockito.mock(InterfaceName.class);
// Common when() stubbing
return bean1;
}
#Bean
#Order(2)
public InterfaceName vmpAdapter() {
bean2 = Mockito.mock(InterfaceName.class);
// Common when() stubbing
return bean2;
}
}
#Test
public void test_functionName_mock_invocation1() {
// Arrange --> Act --> Assert
// nullify other functions custom when() stub.
// updating this functions custom when() stub.
verify(MockServicesConfig.bean1, times(1)).functionName("");
}
#Test
public void test_functionName_mock_invocation2() {
// Arrange --> Act --> Assert
// nullify other functions custom when() stub.
// updating this functions custom when() stub.
verify(MockServicesConfig.bean1, times(1)).functionName("");
}
}
This should now work
#SpringBootTest(
classes = Some.class
)
#MockBean(name = BEAN_NAME, classes = TheBeanClass.class)
#MockBean(name = BEAN_NAME_2, classes = TheBeanClass.class)
class SomeTest {
private final Some some;
#Autowired
SomeTest(Some some) {
this.some = some;
}
}
Please note, if you need to use any of the mocked beans, you will have to put the #Qualifier in the constructor, for example
private final TheBeanClass theBeanclass;
private final Some some;
#Autowired
SomeTest(Some some, #Qualifier(BEAN_NAME) TheBeanClass theBeanClass) {
this.some = some;
this.theBeanClass = theBeanClass;
}
I'm building a Jersey web application deployed on Tomcat. I'm having a hard time understanding how to unit test the app.
Testing the core business logic (the non-Jersey-resource classes) is possible by simply instantiating the classes in my tests and calling methods on them (this has nothing to do with Jersey or Tomcat).
But what is the right way to unit test the Jersey resource classes (i.e., the classes that map to URLs)?
Do I need to have Tomcat running for this? Or should I mock the request and response objects, instantiate the resource classes in my tests, and feed the mocks to my classes?
I have read about testing in Jersey's site, but they're using Grizzly and not Tomcat in their examples, which is different.
Please explain how this should be done. Example code would be welcome.
If you just want to unit test, there's no need to start any server. If you have an services (business layer) or any other injections like UriInfo and things of that nature, you can just mock the. A pretty popular mocking framework is Mockito. Below is a complete example
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
/**
* Beside Jersey dependencies, you will need Mockito.
*
* <dependency>
* <groupId>org.mockito</groupId>
* <artifactId>mockito-core</artifactId>
* <version>1.10.19</version>
* </dependency>
*
* #author Paul Samsotha
*/
public class SomethingResourceUnitTest {
public static interface SomeService {
String getSomethingById(int id);
}
#Path("something")
public static class SomethingResource {
private final SomeService service;
#Inject
public SomethingResource(SomeService service) {
this.service = service;
}
#GET
#Path("{id}")
public Response getSomethingById(#PathParam("id") int id) {
String result = service.getSomethingById(id);
return Response.ok(result).build();
}
}
private SomethingResource resource;
private SomeService service;
#Before
public void setUp() {
service = Mockito.mock(SomeService.class);
resource = new SomethingResource(service);
}
#Test
public void testGetSomethingById() {
Mockito.when(service.getSomethingById(Mockito.anyInt())).thenReturn("Something");
Response response = resource.getSomethingById(1);
assertThat(response.getStatus(), is(200));
assertThat(response.getEntity(), instanceOf(String.class));
assertThat((String)response.getEntity(), is("Something"));
}
}
See Also:
How to get instance of javax.ws.rs.core.UriInfo
If you want to run an integration test, personally I don't see much difference whether or not you are running a Grizzly container vs. running a Tomcat container, as long as you are not using anything specific to Tomcat in your application.
Using the Jersey Test Framework is a good option for integration testing, but they do not have a Tomcat provider. There is only Grizzly, In-Memory and Jetty. If you are not using any Servlet APIs like HttpServletRequest or ServletContext, etc, the In-Memory provider may be a viable solution. It will give you quicker test time.
See Also:
junit 4 test case to test rest web service for some examples, aside from the ones given in the documentation.
If you must use Tomcat, you can run your own embedded Tomcat. I have not found much documentation, but there is an example in DZone. I don't really use the embedded Tomcat, but going of the example in the previous link, you can have something like the following (which has been tested to work)
import java.io.File;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.Response;
import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
/**
* Aside from the Jersey dependencies, you will need the following
* Tomcat dependencies.
*
* <dependency>
* <groupId>org.apache.tomcat.embed</groupId>
* <artifactId>tomcat-embed-core</artifactId>
* <version>8.5.0</version>
* <scope>test</scope>
* </dependency>
* <dependency>
* <groupId>org.apache.tomcat.embed</groupId>
* <artifactId>tomcat-embed-logging-juli</artifactId>
* <version>8.5.0</version>
* <scope>test</scope>
* </dependency>
*
* See also https://dzone.com/articles/embedded-tomcat-minimal
*
* #author Paul Samsotha
*/
public class SomethingResourceTomcatIntegrationTest {
public static interface SomeService {
String getSomethingById(int id);
}
public static class SomeServiceImpl implements SomeService {
#Override
public String getSomethingById(int id) {
return "Something";
}
}
#Path("something")
public static class SomethingResource {
private final SomeService service;
#Inject
public SomethingResource(SomeService service) {
this.service = service;
}
#GET
#Path("{id}")
public Response getSomethingById(#PathParam("id") int id) {
String result = service.getSomethingById(id);
return Response.ok(result).build();
}
}
private Tomcat tomcat;
#Before
public void setUp() throws Exception {
tomcat = new Tomcat();
tomcat.setPort(8080);
final Context ctx = tomcat.addContext("/", new File(".").getAbsolutePath());
final ResourceConfig config = new ResourceConfig(SomethingResource.class)
.register(new AbstractBinder() {
#Override
protected void configure() {
bind(SomeServiceImpl.class).to(SomeService.class);
}
});
Tomcat.addServlet(ctx, "jersey-test", new ServletContainer(config));
ctx.addServletMapping("/*", "jersey-test");
tomcat.start();
}
#After
public void tearDown() throws Exception {
tomcat.stop();
}
#Test
public void testGetSomethingById() {
final String baseUri = "http://localhost:8080";
final Response response = ClientBuilder.newClient()
.target(baseUri).path("something").path("1")
.request().get();
assertThat(response.getStatus(), is(200));
assertThat(response.readEntity(String.class), is("Something"));
}
}