Mockito Mock and Spy in SpringBoot App - java

I have read quite a few articles/blog/StackOverflow questions, but the confusion regarding Mockito mock and spy still remains. So, I started trying implementing them in a small Spring Boot app. My app has a ProductRepository extending CrudRepository.
Currently I'm testing the repository by mocking ProductRepository as follows
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {RepositoryConfiguration.class})
public class ProductRepositoryMockTest {
#Mock
private ProductRepository productRepository;
#Mock
private Product product;
#Test
public void testMockCreation(){
assertNotNull(product);
assertNotNull(productRepository);
}
#Test
public void testSaveProduct() {
assertThat(product.getId(), is(equalTo(0)));
when(productRepository.save(product)).thenReturn(product);
productRepository.save(product);
//Obviously this will fail as product is not saved to db and hence
//#GeneratedValue won't come to play
//assertThat(product.getId() , is(not(0)));
}
#Test
public void testFindProductById() {
when(productRepository.findOne(product.getId())).thenReturn(product);
assertNotNull(productRepository.findOne(product.getId()));
assertEquals(product, productRepository.findOne(product.getId()));
}
}
The test passes. Is this the right way? I also want to understand how to use #Spy here and Why should I need it? Any specific scenarios related to this is most welcome.
Thanks in advance.

I have taken a look at your tests and there are few things to keep in mind (this is based on my experience so the final call is up to you):
1) Hamcrest - If that is possible i would strongly recommend using Hamcrest for your assert implementations.
First of all it is much more versatile and feature rich than the standard junit assertions.
Second of all you may one day have the need (as i did on one of my projects) to switch from junit to testng for examle.
Having all your assertions based on a xunit-neutral implementation, the switch will not be so painful.
2) Assertions - Instead of assertNull, assertEquals go for the hamcrest's assertThat(String description, T value, Mathcer<T> matcher);
Thanks to that you will get clear error messages when a test breaks.
3) Small tests - In your Repository test.. do not put all the cases in one test.
Try to create many small and simple tests for cases like: findOne.. count.. findAll etc.
Again it will be easier to find the problem when a small test like that breaks.
And if more cases will come you wont end up with a 200+ lines of a test case which is unacceptable
4) Naming - Do not name your tests as.. testXYZ.
It is obvious that these are test methods.
I would recommend using the BDD way of naming: shouldX_whenY_givenZ.
F.e. shouldReturnZeroCount_givenNoEntitiesInTheDatabase
5) Structure - Try to split each of your test implementation in three explicit sections, including comments for best result:
public void should..() throws Exception{
// Arrange
// when().then()
// mock()
// Act
// classUnderTest.process()
// Assert
// assertThat(..)
}
6) Do not split your test classes between Mock / Spy test. HAve one ImplTest.
7) Never mock a class which you are testing. In the worst case scenario use Spy if you have to mock some of the methods of the class under test.
The point of mocking is to isolate the implementation in the class under test, so that only that classes logic is invoked during the test.
Mock only dependencies of the class.

Related

Change the behavior of a method to use in unit Test in Java

my problem is that my unit test are slow because I'm publishing in a topic in those unit test, I would like to mock or change its behavior in some way. I was thinking in use reflection for this class and change the method behavior but I'm not sure if that is possible.
This is the behavior that I like to mock or change:
TopicCall.builder()
.toTopic(XXXX)
.withAttribute(XXXXXX, XXXXX)
.withAttribute(XXXXX, XXXXXX)
.withAttribute(XXXXX,XXXXX)
.publish();
I would like to do this because publis() is a real invocation and the test is slow and causing some problems in jenkins, because several unit test are publishing at the same time.
The Topic class is a public class with a static builder method which return a class instance, just like the next one:
public static TopicCall builder() {
return new TopicCall();
}
My problem is that I just acceding the method of this class from outside and I'm not sending the class in the constructor as example and I'm not able to mock its behavior, I'm not able to modify the TopicCall class because it is a .class utility from a jar, besides that I'm not able to use PowerMockito or another library, just Mockito, is there any way to achieve that?
Thanks!
Disclaimer: I missed the fact that PowerMock is forbidden for the author, but the answer could be useful for other users with the same problem.
PowerMock
As far as you want to mock a static method, then Mockito is not the solution.
This could be done using PowerMock.
PowerMock uses ClassLoader way for mocking, which could significantly increase tests time to run.
Here is an examle on Baeldung how to mock static methods.
Solution scratch:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ TopicCall.class })
public class Test {
#Test
void test() {
mockStatic(TopicCall.class);
when(TopicCall.builder()).thenReturn(/*value to be returned*/ null);
// the test code...
}
}

How to set Mock to have a default behavior and can override it in some test

I want to mock a dependency and return a default value in most test cases since most of them should not care about the values returned but there are some certain cases like I would like to test like the dependency returns some weird values or just throw. So I am modeling it in this way. Most cases, it should return a nice and valid value.
Test Setup which return the 20L by default for all test classes.
Dependency dependency = Mockito.mock(Dependency.class);
when(dependency.returnSomeVal()).thenReturn(20L);
In a specific test cases class, I would like to override the behavior like below:
when(dependency.returnSomeVal()).thenThrow(); //failure cases
when(dependency.returnSomeVal()).thenReturn(Weird_Val); //failure cases
But I don't find a good solution to override the existing behavior? Any idea?
You can reset the mock and add behavior. In the test, do
Mockito.reset(dependency);
when(dependency.returnSomeVal()).thenThrow(); //failure cases
when(dependency.returnSomeVal()).thenReturn(Weird_Val); //failure cases
Resetting will remove all mocked behavior on this class though. If you want to remock only some methods, then you have to create the mock from scratch.
I ended using myself this pattern to mock a bunch of methods of a class providing configurations.
In a #Before method I setup a bunch of stubs for a mocked object that provide a correct configuration for each test. Afterwards, in each test it was extremely convenient to only override one of those stubs to provide a different configuration and test a different error case.
I think the response from Hari Menon is correct but it somehow defeats the purpose explained in the question. If the mock is reset, all the stubs would need to be added again, making this pattern very confusing (it would be better to not use any overriding than using reset in this case, the code would be way more straightforward).
The comments added to the question provide indeed an indirect answer on how to achieve this, and why it works, but it took me a bit to get it working.
In spite of one of the comments, I made everything work by using in my #Before fixture when().thenReturn() and overriding the concrete stub with doReturn().when()
Example:
public class WorkerTest {
private ConfigProvider mockedConfigProvider = mock(ConfigProvider.class);
#Before
public void setup() {
// Setup stubs with a correct config
when(mockedConfigProvider.getValue("property1")).thenReturn("value1");
when(mockedConfigProvider.getValue("property2")).thenReturn("value2");
when(mockedConfigProvider.getValue("property3")).thenReturn("value3");
when(mockedConfigProvider.getValue("property4")).thenReturn("value4");
}
#Test
public void test_GoodConfig(){
// The config object gets injected in the test worker
Worker testWorker = new Worker(mockedConfigProvider);
// testWorker.execute() returns true if everything went well
assertTrue(testWorker.execute());
}
#Test
public void test_BadConfigProp1(){
// Test now with a broken 'property1', overriding that stub.
doReturn(null).when(mockedConfigProvider).getValue("property1");
Worker testWorker = new Worker(mockedConfigProvider);
// testWorker.execute() returns false if there is a problem.
assertFalse(testWorker.execute());
}
#Test
public void test_BadConfigProp2(){
// This test needs to only override the result of property2
doReturn("crazy result").when(mockedConfigProvider).getValue("property2");
...
}

Testing with mocks and without Mocks, time of tests

I am learning TDD and I have some doubts when usings Mocks with mockito and junit.
Without mocks:
Contacto contacto = new Contacto("Santiago", "3022653192", "santi-1524#hotmail.com");
agenda.agregarContacto(contacto);
Assert.assertEquals(1, agenda.getSizeOfAgenda());
agenda.agregarContacto(contacto);
With mocks:
Mockito.verify(agenda).agregarContacto(contacto);
Mockito.when(agenda.getSizeOfAgenda()).thenReturn(1);
Assert.assertEquals(1, agenda.getSizeOfAgenda());
if I do the test without the mocks it takes less time than doing the test with Test for example: 2ms vs 356ms.
what are the benefits of doing the test with Mockito in this case?
Kind of missing the point of mocking. You use mocks to ensure ( mock ) the behavior of the surrounding services / external dependencies and everything that do not have to do directly with what you want to test.
In your example if you test your agenda service you will mock the contact as its irrelevant what contents it has as long as a contact goes in the agenda. If you also had lets say a calendar in the agenda you would mock that calendar also.
what are the benefits of doing the test with Mockito in this case?
In your case mocking was used in the wrong way.
A clear example of why you want to mock is a Service that will save something to your database.
public class SomeService {
private SomeRepository repo;
public SomeService(SomeRepository repo){
this.repo = repo;
}
public YourClass save(YourClass clazz){
repo.save(clazz);
}
}
public class SomeRepository {
private Connection yourDatabaseConnection;
public YourClass save(YourClass clazz){
yourDatabaseConnection.save(clazz);
}
}
So lets not bother how the connection is setup. When you will run a test from SomeService this will call your repo thus saving something in your database. Meaning you will have to setup a database and take care of the data inside which may create problems if you run multiple tests or tests in parallel ( if you and one of your colleges run the same test with you at the same time ). All this is a fuss and goes outside the unit-testing theory. Your unit is the Service in this scenario the SomeService. So you only need to test this.
#Test
public void test(){
final SomeRepository repo = mock(SomeRepository.class);
final SomeService serv = new SomeService(repo);
final YourClass clazz = mock(YourClass.class);
when(repo.save(clazz)).thenReturn(clazz);
YourClass response = serv.save(clazz);
Assert.assertEquals(clazz,response);
verify(repo).save(clazz);
}
This way you validate your service returns your object without any mutations in the middle as this was the expected behavior from the setup above. You validate it called the repository but you never actually calling it. This way no data will be added in your database ( meaning you dont need a database ) and the test is isolated and can be run in parallel a million times and it always have the same result.

A Parameterized Parameterized test or Parameterized Suite with a before and after

I have an #Parameterized JUnit test which all works. Now I'm trying to create a new test that runs that same #Parameterized test once for 20 database configurations.
Been having a look online and there are various people who have asked for this but there doesn't seem to be a satisfactory solution. I had a look at Suite but it can't run Parameterized test and it doesn't have any annotations that can be run in between the suite tests to prepare for the next suite test.
Standard Parameterized class:
#RunWith(Parameterized.class)
public class MyParameterizedTest {
// works as normal
}
Here is some pseudo code of what I am looking for:
#RunWith(RunParameterizedLots.class)
#ParameterizedClassToTest(MyParameterizedTest.class)
public class RunParameterizedLotsOfTimes<T> {
#ListOfTestStuff
public List<T> getList() {
return list of T;
}
#BeforeRunningMyParameterizedTest
public void beforePtest(T i) {
setupDatabaseConfig(i);
}
#AfterRunningMyParameterizedTest
public void afterPtest() {
teardownDatabaseConfig(i);
}
}
None of this is written I have spent 2 days reading JUnit documentation and don't seem to be any closer. Release notes for 4.12 talk about #UseParametersRunnerFactory which looks like it might help, there is also some stuff with the new BlockJUnit4ClassRunnerWithParameters but there doesn't seem to be any example of how to use it.
I could create a "#RunWith(Parameterized.class)" class that feeds 1,2,3,4 into itself but then how to I link the sub-parameterized tests into the JUnit subsystem so it looks nice and provides sensible display.
I hope you can see from my waffle question what I am trying to do I just don't know where to start or if someone else has done this work already?
Out of the box it's not possible to parameterize a JUnit test suite, because both are Runners and you may only have one #RunWith() annotation at your test.
For that cause I wrote a new Runner that combines the functionality of both to a ParameterizedSuite: https://github.com/PeterWippermann/parameterized-suite
Good news is, it also enables you to use #Before and #After!
However, I'm not sure if your problem isn't somewhat different: Don't you have two parameters? You say, you already have a parameterized test and now you wan't to run that test against a list of database configurations. So that would be your second parameter. You would like to test all combinations of those two parameters, wouldn't you?

How to get a collection of tests in a JUnit 4 test suite

In JUnit 3, I could get all of the tests within a test suite with the following code:
TestSuite allTestsSuite = (TestSuite) AllTests.suite()
Enumeration enumeration = allTestsSuite.tests();
ArrayList listOfTests = Collection.list(enumeration);
However, I can't find an equivalent way of doing this in JUnit 4. Classes no longer have a .suite() method; they simply use the #Suite annotation. This wouldn't be a problem except that the Suite class no longer has a tests() method. There is a children() method, but that returns a list of Runners, which seem to be something different than why I'm looking for.
So how can I get the tests within a test suite in JUnit 4, like I could with JUnit 3?
The simplest way to perform any kind of filering is to create your own JUnit Categories.
See this Junit Category tutorial for more details but basically, you create your own categories named whatever you want
public interface GuiTest{ }
public interface DbTest { }
And now you can annotate either entire test classes or individual tests with that category:
#Category(GuiTest.class)
public void myJPanelTest{
#Test
public void testFoo(){
...
}
//look we can have other categories too
#Test
#Category(DbTest.class)
public void accidentalDbTest(){
}
}
Then in your test suite, you can specify to include or exclude tests that match the given category
#RunWith(Categories.class)
#IncludeCategory(GuiTest.class)
#ExcludeCategory(DbTest.class) //not sure if we need both but can't hurt
#SuiteClasses( {
...
})
public class GuiTestsOnlySuite{}
Using Categories is much better than having to come up with manually filtering tests based on ad-hoc naming conventions because it that is hard to remember to do (and to make sure everyone in your group adheres to the naming conventions) and since the categories are classes, you can use your IDE to search/refactor/ compile time check your category names.
The only downside I've seen is at least in my IDE, the tests take a little longer to run because there is extra reflection work to do to make sure the test matches your category filter criteria before it runs.
After a bit of experimentation, I discovered the following solution:
SuiteClasses suiteClassesAnnotation = AllTests.class.getAnnotation(SuiteClasses.class);
if (suiteClassesAnnotation == null)
throw new NullPointerException("This class isn't annotated with #SuiteClasses");
Class<?>[] classesInSuite = suiteClassesAnnotation.value();
Basically, it gets the classes the same way that JUnit itself gets them: by looking into the annotation and determining which values are included within it.
The category solution provided by dkatzel is also a good option if you're ultimately wanting to filter these classes, but if you need a list of classes in a suite for some other purpose such as code analysis, this is the simplest and most direct way to do it.

Categories