I am working with Spring 4.3.1.RELEASE and I have the following about testing:
#RunWith(Parameterized.class)
#ContextConfiguration(classes={RootApplicationContextConfig.class})
#Transactional
public class PersonaJdbcRepositoryTest {
#ClassRule
public static final SpringClassRule SPRING_CLASS_RULE= new SpringClassRule();
#Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
#Autowired
private PersonaRepository personaJdbcRepository;
private final Persona persona;
public PersonaJdbcRepositoryTest(Persona persona){
this.persona = persona;
}
#Parameters
public static Collection<Persona[]> data() {
return Arrays.asList(new Persona[][] {
{PersonaFactory.crearPersona01()},
{PersonaFactory.crearPersona02()},
{PersonaFactory.crearPersona03()},
{PersonaFactory.crearPersona04()},
{PersonaFactory.crearPersonaMix()}
});
}
#Test
#Sql(scripts={"classpath:/com/manuel/jordan/h2/h2-elimination-script.sql"})
public void saveOneTest(){
Persona personaSaveOne = personaJdbcRepository.saveOne(persona);
Persona personaFindOne = personaJdbcRepository.findOne(personaSaveOne.getId());
assertEquals(personaFindOne, personaSaveOne);
}
#Test
public void findOneTest(){
Persona personaFindOne = personaJdbcRepository.findOne(persona.getId());
assertEquals(personaFindOne, persona);
}
#Test
public void findAllTest(){
Collection<Persona> personas = PersonaFactory.crearPersonas();
Collection<Persona> personasFindAll = personaJdbcRepository.findAll();
assertEquals(personas.size(), personasFindAll.size());
}
}
I can confirm that each #Test is executed and pass N times (in this case 5) according with data().
My requirement is that: I only need one execution of the findAllTest() method. I mean, it is executed 4 times unnecessarily. Even more when it does not use the persona object.
Therefore is possible indicate through SpringClassRule/SpringMethodRule that a #Test method just works once? How?
Is it possible to indicate through SpringClassRule/SpringMethodRule that a #Test method just runs once?
No. That is not possible.
The behavior you are experiencing has nothing to do with Spring.
It is the Parameterized runner that controls how many times a test method is executed.
If you don't want a test method to be parameterized, simply move it to a different test class that does not use the Parameterized runner.
Related
I am very new with Mockito and I don't get the following example (classes were provided, only test to write) and how to solve it.
What I try to do is use a test double for the supplier so that we can control the returned greeting in the test and assert that the GreetingService does not modify the greeting message in any way. Then assert that the returned greeting string is equal to "Hello Andy.".
public class Greeting {
private final String template;
public Greeting(String template) {
this.template = template;
}
public String forName(String world) {
return String.format(template, world);
}
}
#Component
public class GreetingService {
private final Supplier<Greeting> greetingSupplier;
public GreetingService(Supplier<Greeting> greetingSupplier) {
this.greetingSupplier = greetingSupplier;
}
public String greet(String name) {
return greetingSupplier.get().forName(name);
}
}
#Component
public class RandomGreetingSupplier implements Supplier<Greeting> {
private final List<Greeting> greetings = Arrays.asList(
new Greeting("Hello %s."),
new Greeting("Hi %s!"),
);
private final Random random = new Random();
#Override
public Greeting get() {
return greetings.get(random.nextInt(greetings.size()));
}
}
#SpringBootTest
public class GreetingServiceTest {
#Autowired
GreetingService greetingService;
#MockBean
Supplier<Greeting> greetingSupplier;
#Test
void getGreetingForPerson() {
String name = "Andy";
// that test cannot know which greeting will be returned by the supplier
// WHY IS IT NULLPOINTEREXCEPTION AFTER INITIALIZING #MockBean
//String greeting = greetingService.greet(name);
//assertThat(greeting).contains(name);
// WROTE SUCH TEST HERE -> NullPointerException WHY?
Mockito.when(greetingSupplier.get().forName(name)).thenReturn("Hello %s.");
assertThat(greetingSupplier.equals("Hello Andy."));
// THIS IS WORKING & TEST PASSED BUT I GUESS ITS WRONG?
Mockito.when(greetingSupplier.get()).thenReturn(new Greeting("Hello %s."));
assertThat(greetingSupplier.equals("Hello Andy."));
}
}
Mockito.when(greetingSupplier.get().forName(name)).thenReturn("Hello %s.");
You can't chain calls like that, you need to produce intermediate results, like
Supplier<Greeting> supplier = mock(Supplier.class);
Mockito.when(supplier).forName().thenReturn("Hello %s.");
Mockito.when(greetingSupplier.get()).thenReturn(supplier);
For dependency injection, you need to create the subject under test with the mocked Supplier. You can do that in a #Before method for example.
Your mocking is wrong.
Mockito.when(greetingSupplier.get().forName(name)).thenReturn("Hello %s.");
You mocked Supplier<Greeting> and the default behavior is to return null. So when you call greetingSupplier.get() in your first line it returns null. You directly chain forName which nou basicall is null.forName which leads to an error.
Your second part is actually (kind of) correct.
Mockito.when(greetingSupplier.get()).thenReturn(new Greeting("Hello %s."));
You now properly return a response from greetingSupplier.get(). Instead of chaining the call.
However I would argue that your excercise is wrong. Why? When using a Supplier<?> in Spring it actually is a lazy beanFactory.getBean call. You can lazily inject dependencies this way. You should have a mock for Greeting which returns a hardcoded String which you can check.
Hi I have created a spring boot project with a rest end point which is going to return all the files list inside a cloud storage.
I'm unable to write a junit test case for that. could you please help me on the junit.
Controller Class
#GetMapping("/getFiles")
public List<String> getBucketList() throws IOException {
Storage storage =
StorageOptions.newBuilder().setCredentials(ServiceAccountCredentials.getApplicationDefault())
.setProjectId("projectId") // project id
.build().getService();
return mainService.getFileList(storage);
Service class
#Service
public class MainServiceDetails {
public List<String> getFileList(Storage storage) {
List<String> list = new ArrayList<>();
Page<Blob> blobs = storage.list("bucketname"); // bucket name
for (Blob blob : blobs.iterateAll()) {
list.add(blob.getName());
System.out.println(blob.getName());
}
return list;
}
}
I need minimum 90% code coverage in this one. Could anyone please help me on this.
I don't know where you got stuck in your unit test but this is how I would test it, I hope this will help you:
#ExtendWith(MockitoExtension.class)
public class ControllerTest {
#Mock
private MainServiceDetails mainService;
#InjectMocks
private Controller controller;
#Captor
private ArgumentCaptor<Storage> storageCaptor;
#Test
public void test() {
List<String> expected = Arrays.asList("sup1", "sup2", "sup3");
Mockito.when(mainService.getFileList(storageCaptor.capture()))
.thenReturn(expected);
List<String> actual = controller.getBucketList();
Storage value = storageCaptor.getValue();
//verify call to mainService is made with Storage.
Mockito.verify(mainService, times(1)).getFileList(value);
//Add some assertions to check if the storage object has the values you expect
//assertThat(value.getProjectId()).isEqualTo("projectId");
}
}
i have a DAO class that I am trying to write test cases
Here is modified reproducible code
the DAO class I am trying to test
#Component
public class SomeDAO {
#Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public boolean dataInTable(String id) {
String sql = "SELECT COUNT(*) " +
"FROM table1" +
"WHERE id =:id";
MapSqlParameterSource sqlParms = new MapSqlParameterSource();
sqlParms.addValue("id", id
try {
int count = namedParameterJdbcTemplate.queryForObject(sql, sqlParms, Integer.class);
return (count > 0) ? true : false;
} catch (Exception e) {
log.error("Error checking existence of id : " + id);
throw e;
}
}
}
When i run the test, it throws an NullPointer on
int count = namedParameterJdbcTemplate.queryForObject(sql, sqlParms, Integer.class);
so i believe mockito is not returning a value on when, thenReturn
i searched around stack overflow and it looks like what i have should work, but I am getting NullPointerException.
I have also tried using the same value for sql string and a mapObject as the dao class instead of anyString() and anyMap()
but did not work either
Test case
#RunWith(MockitoJUnitRunner.class)
public class SomeDAOTest {
#InjectMocks
SomeDAO someDao
#Mock
NamedParameterJdbcTemplate namedParameterJdbcTemplate;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
#Test
public void TestdataInTable(){
//
when(namedParameterJdbcTemplate.queryForObject(anyString(), anyMap(),eq(Integer.class))).thenReturn(1);
assertEquals( true,someDao.dataInTable("123456"));
}
}
Try this,
#Test
public void TestdataInTable() {
Mockito.when(namedParameterJdbcTemplate.queryForObject(Mockito.anyString(),
Mockito.any(SqlParameterSource.class), Mockito.eq(Integer.class))).thenReturn(1);
assertEquals(true, someDao.dataInTable("123456"));
}
The second parameter in queryForObject() is of type SqlParameterSource, which is not directly a map. So it should be, Mockito.any(SqlParameterSource.class).
The current version of your code displays warning in console, which is the best way to debug the issue.
[MockitoHint] TestDao.TestdataInTable (see javadoc for MockitoHint):
[MockitoHint] 1. Unused... -> at TestDao.TestdataInTable(TestDao.java:33)
[MockitoHint] ...args ok? -> at SomeDao.dataInTable(SomeDao.java:26)
Adding this as it may help someone:
I had a similar issue but the reason was very different.
I was getting null pointer exception not just on NamedParameterJdbcTemplate which was annotated with #Mock but also on #InjectMocks.
It so turn out that I was using Junit 5. The above solution will work with Junit 4. To make Mockito use Junit 5, an additional annotation is required. Also make sure you remove #RunWith(MockitoJUnitRunner.class) as this will cause failure. Instead use #ExtendWith(MockitoExtension.class.
Here is an example:
#ExtendWith(MockitoExtension.class)
public class SomeDAOTest {
#InjectMocks
SomeDAO someDao
#Mock
NamedParameterJdbcTemplate namedParameterJdbcTemplate;
I use spring boot 2.
I search to test a private method in the facade
#RunWith(SpringRunner.class)
#SpringBootTest
public class SamplingsFacadeTest {
#Autowired
private SamplingsFacade facade;
#MockBean
private SamplingsService samplingsService;
#Test
public void exampleTest() throws Exception {
List<Samplings> samplings = new ArrayList<>();
Samplling sampling = new Samplings();
..
samplings.add(sampling);
//fake call
Mockito.doReturn(samplings).when(samplingsService.getSamplingContainingNonCompliantTest());
//real call
List<FactoryEmailNCDto> factoryEmails = Whitebox.invokeMethod(facade, "prepareDataNoncompliantSampling");
}
public List<Samplings> getSamplingContainingNonCompliantTest() {
return samplingsRepository.findSamplingContainingNonCompliantTest();
}
In Facade In
private List<FactoryEmailNCDto> prepareDataNoncompliantSampling() {
List<FactoryEmailNCDto> factoryEmailNC = new ArrayList<>();
List<Samplings> samplings = samplingsService.getSamplingContainingNonCompliantTest();
for (Samplings sampling : samplings) {
...
}
}
Why when I debug, samplings is null and not the value I created
Mockito.doReturn(samplings)
.when(samplingsService.getSamplingContainingNonCompliantTest());
One potential problem is that doReturn takes the form doReturn(value).when(mock).method(), not doReturn(value).when(mock.method()). The latter is considered an incomplete stubbing. Instead, it should look like this:
Mockito.doReturn(samplings)
.when(samplingsService)
.getSamplingContainingNonCompliantTest();
Note that there may be other problems with your test; the code you've written expects samplingsService to be public and non-final, and your getSamplingContainingNonCompliantTest() to likewise be public, non-static, and non-final, but the code sample you have does not confirm that. You may want to call Mockito.validateMockitoUsage() in an #After method, or use a #Rule that handles Mockito annotations and cleanup for you.
I am using SpringBoot 1.5.9 and try to do Integration testing. Weirdly a MongoRepository.save() method updates the object when called on mock MongoRepository.
I have a Counter Class
public class Counter {
public String id;
public int seq;
public void increaseSeq() {
this.seq += 1;
}
}
And his repository
public interface CounterRepository extends MongoRepository<Counter, String>{
Counter findById(String id);
List<Counter> findAll();
}
And his service
#Service
public class CounterService {
#Autowired private CounterRepository counterRepository;
public Counter findCounter(String id) {
return counterRepository.findById(id);
}
public int getSeqAndIncrease(String id) {
Counter counter = findCounter(id);
if (counter == null) {
return -1;
}
counter.increaseSeq();
counterRepository.save(counter);
return counter.getSeq();
}
}
Now, when I do system integration and try to mock the counterRepository, it happens something that I don't expect. The counterRepository.findById() returns a Counter object where the 'seq' field is increased. Why? Does the counterRepository.save() affect the result in any way (the counterRepository is mocked, hence I suppose that save() should not have any effect)?
#RunWith(SpringRunner.class)
#SpringBootTest
public class FlowServiceTest {
#MockBean private CounterRepository counterRepository;
#Autowired private CounterService counterService;
#Before
public void setUp() {
Mockito.when(counterRepository.save(any(Counter.class))).then(arg -> arg.getArgumentAt(0, Counter.class));
Mockito.when(counterRepository.findById("flow")).thenReturn(new Counter("flow", 10));
}
#Test
public void testSavingInDatabase() {
System.out.println(counterRepository.findById("flow"));
counterService.getSeqAndIncreaseSafe("flow");
System.out.println(counterRepository.findById("flow"));
counterService.getSeqAndIncreaseSafe("flow");
System.out.println(counterRepository.findById("flow"));
}
}
It prints "10 11 12". Why doesn't it print '10 10 10'?
The problem is these lines
counterRepository.save(counter);
return counter.getSeq();
What you should be doing is this
Counter saveCounter = counterRepository.save(counter);
return savedCounter.getSeq();
In getSeqAndIncrease method, you are not returning sequence of the saved object.
By doing this you are making your mock statement for save useless. Because you are not using the value returned from mock.
tl;dr - The returned object from mock is initialized only once in mockito. So I basically got the same reference every time, and since it is a reference not a new object, the values are updated.
Complete answer: When setting
Mockito.when(counterRepository.findById("flow")).thenReturn(new Counter("flow", 10));
, it might seem intuitive to return a new object every time, but the return object is initialised only once when the test starts and will be returned at all subsequent calls.
Then, in my code I do
counter.increaseSeq();
which increases the 'seq' of found object (this object comes from Mockito). Then at the next call, Mockito returns the firstly initialised object which was updated in the meantime; Mockito does not return a new object as it might seem like.