So I am making my first tests in spring boot and I came across a problem. When I execute my tests, the values are actually getting deleted. I would prefer to mock this so that the values do not get deleted.
My test class:
#SpringBootTest
#AutoConfigureMockMvc
public class PartyEndpointTest {
#Autowired
private MockMvc mockMvc;
#Test
#DisplayName("Should Not give access to endpoint")
public void ShouldNotGiveAccess() throws Exception{
mockMvc.perform(MockMvcRequestBuilders.get("/parties"))
.andExpect(MockMvcResultMatchers.status().is(401));
}
#Test
#WithMockUser("JLardinois")
#DisplayName("Should respond with not found request")//is 401 because first authorized then check if its found
public void shouldNotFindRequest() throws Exception{
mockMvc.perform(MockMvcRequestBuilders.get("/partyy")).andExpect(MockMvcResultMatchers
.status().is(404));
}
#Test
#WithMockUser("JLardinois")
public void ShouldFindRequest() throws Exception{
mockMvc.perform(MockMvcRequestBuilders.get("/parties")).andExpect(MockMvcResultMatchers
.status().isOk());
}
#Test
#WithMockUser("JLardinois")
public void DeleteParty() throws Exception{
mockMvc.perform(MockMvcRequestBuilders.delete("/parties/4")).andExpect(MockMvcResultMatchers
.status().isOk());
}
In this test class, my delete method from my controller gets tested, but the entity with ID 4 in my database gets deleted when I run this. I do not want this.
Can anyone help me how to mock this, so that nothing gets deleted from my database?
Thanks in advance!
Firstly, you should not run tests on your real database. Tests should not be related to the environment in any way. If you want integration tests with database, pay attention to testcontainers, you can choose framework and database what you want. Your test configuration should not contain the actual base values, create test configuration (application.yaml in src/test/resources) if you don't have it.
If you don't want integration test, mock your repository:
#MockBean
private MyRepository myRepository;
#Before
public void init() {
Mockito.doNothing().when(myRepository).delete(any());
}
Related
I am working on creating integration test for a service class i am testing and I needed to mock the dao for one of the test methods. the problem is when i run the tests together some of my tests fail but when i run them individually the tests past. If i remove the mockito part all my tests pass when i run them all at once. any insight on this is appreciated
below is my code:
// here is my Service class
public class Service {
Dao dao;
public Dao getDao() {
return dao;
}
public void setDao(Dao dao) {
this.dao = dao;
}
}
//here is my integ test
#Category(IntegrationTest.class)
#RunWith(SpringRunner.class)
public class Test{
#Rule
public ExpectedException thrown = ExpectedException.none();
#Autowired
#Qualifier(Service.SERVICE_NAME)
protected Service service;
#Before
public void setUp() {
assertNotNull(service);
}
#Test
public void testDoSomethingOne() throws Exception {
Dao dao = Mockito(Dao.class)
service.setDao(dao)
boolean flag = service.doSomething();
Assert.assertTrue(flag);
}
#Test
public void testDoSomethingTwo() throws Exception {
Integer num = service.doSomething();
Assert.assertNotNull(num);
}
The test method testDoSomethingOne() sets the mock dao for the service instance which it retains for rest of the tests.
Annotate the method testDoSomethingOne() with #DirtiesContext to get a fresh context associated with the subsequent test method.
Test annotation which indicates that the ApplicationContext associated
with a test is dirty and should therefore be closed and removed from
the context cache.
Use this annotation if a test has modified the
context — for example, by modifying the state of a singleton bean,
modifying the state of an embedded database, etc. Subsequent tests
that request the same context will be supplied a new context.
You can get the dao before each test and assign it back to service after the test
something like this:
private static Dao dao;
#Before
public void setUp() {
if(dao == null) {
dao = service.getDao();
}
}
#After
public void tearDown() {
service.setDao(dao);
}
If it is a integration test you should not mock your daos, the recommended way is to use a in memory database like H2. The spring folks already provide the annotation #DataJpaTest that creates the database for you.
You can use the #DataJpaTest annotation to test JPA applications. By default, it scans for #Entity classes and configures Spring Data JPA repositories. If an embedded database is available on the classpath, it configures one as well. Regular #Component beans are not loaded into the ApplicationContext.
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-autoconfigured-jpa-test
I have this quite simple controller class and a simple (jpa) repository.
What I want to do is to test it's api but mock it's repository and let it return an object or not depending on the test case.
My problem now is that I don't know how to do that.
I know how to mock a repository and inject it to a controller/service class with #Mock / #InjectMocks / when().return()
But I fail when I want to do the same after doing a request with MockMvc.
Any help is highly appreciated
The controller
import java.util.Optional;
#RestController
#Slf4j
public class ReceiptController implements ReceiptsApi {
#Autowired
private ReceiptRepository receiptRepository;
#Autowired
private ErrorResponseExceptionFactory exceptionFactory;
#Autowired
private ApiErrorResponseFactory errorResponseFactory;
#Override
public Receipt getReceipt(Long id) {
Optional<ReceiptEntity> result = receiptRepository.findById(id);
if (result.isEmpty()) {
throw invalid("id");
}
ReceiptEntity receipt = result.get();
return Receipt.builder().id(receipt.getId()).purchaseId(receipt.getPurchaseId()).payload(receipt.getHtml()).build();
}
private ErrorResponseException invalid(String paramName) {
return exceptionFactory.errorResponse(
errorResponseFactory.create(HttpStatus.NOT_FOUND.value(), "NOT_VALID", String.format("receipt with id %s not found.", paramName))
);
}
}
And it's test class
#WebMvcTest(ReceiptController.class)
#RestClientTest
public class ReceiptControllerTest {
#InjectMocks
private ReceiptController receiptController;
#Mock
private ReceiptRepository receiptRepository;
#Mock
private ErrorResponseExceptionFactory exceptionFactory;
#Mock
private ApiErrorResponseFactory errorResponseFactory;
private MockMvc mvc;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders.standaloneSetup(
new ReceiptController())
.build();
}
#Test
public void getReceiptNotFoundByRequest() throws Exception {
mvc.perform(MockMvcRequestBuilders
.get("/receipt/1")
.header("host", "localhost:1348")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isNotFound());
}
//TODO: Finish this test
#Test
public void getReceiptFoundByRequest() throws Exception {
ReceiptEntity receipt1 = ReceiptEntity.builder().id(99999L).purchaseId(432L).html("<html>").build();
when(receiptRepository.findById(1L)).thenReturn(Optional.of(ReceiptEntity.builder().id(1L).purchaseId(42L).html("<html></html>").build()));
ResultActions result = mvc.perform(get("/receipt/1")
.header("host", "localhost:1348")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
Within your setUp() method, you're using Mockito to mock the beans annotated with #Mock and inject them in a new instance of ReceiptController, which is then put into the field annotated with #InjectMocks.
On the next line, you're setting up MockMvc with a new instance of ReceiptController (you use new ReceiptController()), rather than using the instance that Mockito created. That means that all fields within that instance will be null.
This pretty much boils down exactly to Why is my Spring #Autowired field null.
To solve this, you could pass receiptController to MockMvc. In that case, you could also remove the #WebMvcTest and #RestClientTest as you're not using them.
Alternatively, you could setup your test with #RunWith(SpringRunner.class), and properly set up a Spring test by using #Autowired in stead of #InjectMocks and #MockBean in stead of #Mock. In that case, you don't need a setUp() method at all, as MockMvc could be autowired by Spring.
#WebMvcTest and MockMvc allows you to do integration testing, not unit testing.
They allow you to boot an actual Spring application (using a random port) and test the actual controller class with its dependencies. This means that the variables you declared at the top of your test are not actually used in your current setup.
If you wish to unit-test your controller, you can remove #WebMvcTest, create mock dependencies (like you did) and call your methods directly instead of using MockMvc.
If you really wish to write an integration test, you need to mock your external dependencies (the database for example). You can configure spring to use an embedded database (H2 for example) in the test environment, so that you do not affect your real database.
See an example here : https://www.baeldung.com/spring-testing-separate-data-source
How can I call tests from another test?
I have a class in the jar that I have added as dependency into my project:
public class Tests{
private MockMvc mockMvc;
#Test
public void test1() throws Exception {
.....
mockMvc.perform(get(myRequest)
.content(dataFromDB)
.......
}
}
#Test
public void test2() throws Exception {
.....
mockMvc.perform(get(myRequest)
.content(dataFromDB)
.......
}
}
.......
And in my project I have:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = MyApp.class)
public class MyTests {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext context;
#Before
public void init() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();
}
#Test
public void test() throws Exception {
CALL SOMEHOW TESTS FROM THE JAR HERE
}
I want those tests from the jar to test my project's database (for example: dataFromDB should be some data from the project where this dependency has been added).
I have already added this jar and I can call class Tests inside my project,so I have access to it. I am just not sure how to run those tests inside it.
What should I change so it works well? Thanks
Updated:
*I want all tests from the jar to call at the same time, not individually.
*I want to give jar access to my db, so it can get all needed testing data in the db table of my project.
From what is see, you have 2 sets of environment, and 1 set of tests.
So one way to solve this is that you make the environment passable, the mockmvc, the dataFromDb, etc, so that the tests can execute independently of the environment.
I would suggest having the test methods in another class, like this very simplified example for easy reading:
class MyTestMethods {
void test1(TestEnv env, Req myRequest) {
env.getMockMvc()
.perform(env.get(myRequest)
.content(env.getDataFromDB());
// assertions here
}
}
class OldTestInJar {
#Test
public void test1() {
new MyTestMethods().test1(myEnv, myReq);
}
}
class MyNewTest {
#Test
public void test1() {
new MyTestMethods().test1(myNewEnv, myNewReq);
}
}
I am testing a service class which uses a Dao layer under it.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class AppServiceTest {
#Autowired
#InjectMocks
private AppService appService;
private AppConfig appConfig = new AppConfig(), appConfigOut = new AppConfig();
#MockBean //This statement is under inspection in the problem
private AppDao appDao;
#Before
public void setUp() throws Exception {
String appKey = "jsadf87bdfys78fsd6f0s7f8as6sd";
appConfig.setAppKey(appKey);
appConfigOut.setAppKey(appKey);
appConfigOut.setRequestPerMinute(null);
appConfigOut.setRequestDate(DateTime.now());
MockitoAnnotations.initMocks(this);
}
#Test
public void testFetchAppConfigValidParam() throws Exception {
when(appDao.fetchAppConfig(appConfig)).thenReturn(appConfigOut);
assertThat(appService.fetchAppConfig(appConfig)).isEqualToComparingFieldByField(appConfigOut);
}
In the above program when I write #MockBean, the test throws a NullPointerException, but when I write #Mock the test executes successfully. I think the appDao being called is the actual one defined in appService and accessing the database. This is because the time taken by the test is around 200ms and usual test cases for other applications is 60ms-100ms. But I am not sure because other cases where DAO really access data takes 400ms to 500ms.
How do I know mock is actually working and when appService calls the appDao method from inside it is actually the mock. Is there any programmatical way to verify this.
P.S. If #Mock works in this scenario what is #MockBean is useful for in spring boot.
M.Deinum is pointing you in the correct direction in the comment.
Maybe you want to give the spring documentation about Mocking and Spying in tests a read - https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications-mocking-beans
But to answer you question - you can use MockingDetails to tell if an object is a mock.
MockingDetails mockingDetails = org.mockito.Mockito.mockingDetails(appDao)
boolean appDaoIsMock = mockingDetails.isMock()
(https://stackoverflow.com/a/15138628/5371736)
I have an Integration test as shown below, and I want to rollback the changes to the database after going through the test cases. Now my question is is there a way to prevent default rollback from happening after each test case and have a roll back once all the test cases are done.
After surfing a bit I found that TestNG can be helpful, but I don't want to use that. Is there any other alternative?
#TransactionConfiguration(transactionManager = "myTransactionManager", defaultRollback = true)
public class TestDependencies extends testBase {
#Test
public void testSetupData() throws SQLException, Exception{
//Some initial setup code.
}
#Test
public void testFunctionality throws Exception{
//here i will further test some more functionality
}
}
If all your test cases that require rollback are in one class, the junit #afterclass annotation will run this method after all #test methods are executed.
#AfterClass
public static void Cleanup() {
txManager.rollback();
}
txManager should be wired up to your DataSourceTransactionManager bean.