Is the #SpringBootTest test class correct? - java

Is this the correct essence of unit testing? I think i don't understand what should I test. ConverterContext is a strategy class
#SpringBootTest
#ExtendWith(SpringExtension.class)
class ConverterContextTest {
#Autowired
private final ConverterContext converterContext;
#Autowired
private final ConverterRegisterUserDto created;
#Autowired
ConverterContextTest(ConverterContext converterContext, ConverterRegisterUserDto created) {
this.converterContext = converterContext;
this.created = created;
}
#Test
void converterContextGivesCorrectConverter(){
ConverterRegisterUserDto returned = converterContext.getConverter(ConverterRegisterUserDto.class);
assertEquals(returned, created);
}
#Test
void converterContextGivesIncorrectConverter(){
ConverterShowUserDto returned = converterContext.getConverter(ConverterShowUserDto.class);
assertNotEquals(returned, created);
}
}

In a unit test, you want to avoid loading a spring boot context. So instantiate yourself ConverterContext.
If ConverterContext has some dependencies that you need to isolate, you can mock them (see the Mockito library).
Besides you should not need either to autowire the expected.
It will make your test faster to execute and simpler to read.
Note that in your code ConverterContext appears as a factory and not a strategy.
About the logic of your test, I think that the second test is not required.
What you want to check is that the factory returns what it is designed to return.
Asserting that the actual is not equal to a dumb expected is perfectly useless . It is like dead code that you have to main...
In fact, it is like if you asserted that add(1,1) == 2 in the first test and that in the second test you asserted that add(2,1) != 2.
Why not assert that all dumb expected are not equal ?
add(2,1) != 4, add(2,1) != 5, add(2,1) != 6, and we can go on a very long time....
I would expect a unit test that looks like (I used JUnit 5 way to illustrate) :
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.Mock;
import org.junit.jupiter.api.extension.ExtendWith;
#ExtendWith(MockitoExtension.class)
class ConverterContextTest {
ConverterContext converterContext;
#Mock
FooDep fooDep;
#Mock
BarDep barDep;
#BeforeEach // or #Before in JUnit 4
void init{
converterContext = new ConverterContext(fooDep, barDep);
}
#Test
void converterContextGivesCorrectConverter(){
// mock which is required
/...
assertEquals(new ConverterRegisterUserDto(), converterContext.getConverter(ConverterRegisterUserDto.class));
// mock which is required
/...
assertEquals(new ConverterShowUserDto(), converterContext.getConverter(ConverterShowUserDto.class));
}
}

With unit testing the idea is to test the smallest possible piece of code. In most cases this means a class and what its methods do. As for how many tests to write, that really depends on the person. To give an example:
public class Point2D
{
private int x, y;
public Point2D() {this.x = 0; this,y = 0;}
public int getX() {return x;}
public int getY() {return y;}
}
class Point2DTest
{
#Autowired
private final Point2D p;
#Test
void getXReturnsZero()
{
int expected = 0;
assertEquals(expected, p.getX());
}
#Test
void getYReturnsZero()
{
int expected = 0;
assertEquals(expected, p.getY());
}
#Test
void getXDoesNotReturnZero()
{
int expected = 1;
assertNotEquals(expected, p.getX());
}
#Test
void getYDoesNotReturnZero()
{
int expected = 1;
assertNotEquals(expected, p.getY());
}
}
This test could have been left with the first two methods, but for more test coverage a person may test more. I think its important to note that exhaustive testing (testing every possible outcome) is impossible in real life scenarios and generally a cut off point will be defined.

I tend to think of it as more of an integration test when you're loading a Spring context and more of a unit test when you don't and you're testing a specific logic of a small method without any context. When you're trying to test a class that only realistically depends on other injected beans in a Spring context you can use a framework like Mockito in order to keep them unit tests and prevent them from becoming full blown integration tests. I hope that's helpful.

Related

Unit Test: Cannot use ArgumentCaptor

I am trying to create a unit test for the following method that calls another private method:
public CommandDTO create(final LabelRequest request) {
return saveLabel(new Label(), request);
}
private CommandDTO saveLabel(final Label label, final LabelRequest request) {
label.setName(request.getName());
final Label saved = labelRepository.saveAndFlush(label);
return CommandDTO.builder().uuid(saved.getUuid()).build();
}
It returns "nullpointer exception for the saved parameter as I did not mock in in my test.
Here is the Unit Test. I added 2 question as comment (Q1 and Q2). Could you please clarify me what is wrong?
#RunWith(MockitoJUnitRunner.class)
public class LabelServiceImplTest {
#Mock
private LabelRepository labelRepository;
#InjectMocks
private LabelServiceImpl labelService;
#Captor
ArgumentCaptor<Label> labelCaptor;
#Test
public void test_create {
// Q1: I am not sure if the following parts are needed
final LabelRequest request = new LabelRequest();
request.setName("Label Name");
final Label label = new Label();
label.setName(request.getName());
// Q2: I think there is no need to mock saveAndFlush method.
// But in this scene it returns "nullpointer exception"
when(labelRepository.saveAndFlush(any())).thenReturn(label);
CommandDTO result = labelService.create(request);
Mockito.verify(labelRepository).saveAndFlush(labelCaptor.capture());
final Label value = labelCaptor.getValue();
// then make necessary assertions
}
}
F I R S T
I think that you only declared your mocks but never defined/created them. There's basically three ways to do so:
1) mockito extension
The convenient way in JUnit 5 is to use the MockitoExtension:
#ExtendWith(MockitoExtension.class)
public class LabelServiceImplTest {
// declare mocks, spies and captors ...
// system under test
#InjectMocks
LabelService sut;
}
Nothing else should be needed now.
Mind that by #InjectMocks you instruct Mockito to create your system under test and inject the mocks. You may as well do that manually, e. g. by injecting mocks into the SUT's constructor. I prefer that way to have more control over the configuration of my SUT.
2) MockitoAnnotations.openMocks(testclass)
This method will inject mocks into the given object:
public class LabelServiceImplTest {
// declare mocks, spies and captors ...
// system under test
#InjectMocks
LabelService sut;
#BeforeEach
void setup() {
MockitoAnnotations.openMock(this)
// ...
}
}
On JUnit 4 you'd use MockitoAnnotations.initMocks(…) instead.
3) the old school way
public class LabelServiceImplTest {
LabelRepository labelRepositoryMock = Mockito.mock(LabelRepository.class);
// ...
// system under test
LabelServiceImpl sut;
#BeforeEach
void setup() {
sut = new LabelService(labelRepositoryMock);
// ...
}
}
S E C O N D
Your questions …
1) Do you need to create the request and the label?
Yes. You call the service using the request and the mock returns the label. I would suggest you do not use the request's property to set up your label, use a String literal instead.
2) Do you need to mock the saveAndFlush() method?
Yes. This is the part where you configure your SUT's environment to behave in a predictable way.
You might consider postfixing your mocks, spies and captors by: "Mock", "Spy" and "Captor".
You might also consider calling your system under test "sut".
#Mock
private LabelRepository labelRepositoryMock;
#Captor
ArgumentCaptor<Label> labelCaptor;
// system under test
#InjectMocks
private LabelServiceImpl sut;
This makes the test code more readably, I think.

JUnit test for my method

Hello every one I need to write unit tests for my methods. I'm having a bit of trouble because I'm new to JUnit. I need to write a test for this method.
this is my method
#Override
public Long countSellingOrdersInQueue(String principal) {
List<String> states = Arrays.asList(PENDING.name(), REGULARIZED.name());
return orderRepository.countByArticleUserIdAndStateIn(principal, states);
}
I try but i'm blocked and this is my result
P.S. test is passed but I don't understand if my test is true
#MockBean
private OrderRepository orderRepository;
private String principal ;
#Test
public void countSellingOrdersInQueueTest(){
orderService.countSellingOrdersInQueue(principal);
List<String> states = Arrays.asList(PENDING.name(), REGULARIZED.name());
orderRepository.countByUserIdAndStateIn(principal,states);
}
In your case, it is just the unit test, you need not use #MockBean, as it loads the context. Unit tests are meant to be run faster, using #MockBean, will load the context and takes time to complete the test. Here is the suggestion of when to use #Mock and when to use #MockBean.
As Maxim said, there were no assertions in the test. That was the reason why the tests weren't failing.
Few things to keep in mind while writing the test.
Tests are considered as documentation for the code, it should be more readable in such a way it makes others to understand the code.
As said before, unit tests are for giving faster feedback
Have AAA (Arrange, Act, Assert) structure in tests. More info here
Here is the code:
public class OrderServiceTest {
#InjectMocks
private OrderService orderService;
#Mock
private OrderRepository orderRepository;
#Before
public void setUp() throws Exception {
initMocks(this);
}
#Test
public void countSellingOrdersInQueueTest(){
when(orderRepository.countByArticleUserIdAndStateIn(any(), any())).thenReturn(1L);
String principal = "dummyString";
Long actualCount = orderService.countSellingOrdersInQueue(principal);
List<String> expectedStates = Arrays.asList("State 1", "State 2");
assertThat(actualCount, is(1L));
verify(orderRepository).countByArticleUserIdAndStateIn(principal, expectedStates);
}
}
Test passes because you don't have any assertion, which checks result. You just invoke methods which executes without exception.
Simple test example:
#Test
public void test() {
assertEquals(true, true);
}
In your case test will be look likes:
#Test
public void countSellingOrdersInQueueTest(){
orderService.countSellingOrdersInQueue(principal);
List<String> states = Arrays.asList(PENDING.name(), REGULARIZED.name());
orderRepository.countByUserIdAndStateIn(principal,states);
assertEquals(10, orderRepository.countByUserIdAndStateIn(principal,states));//10 replace to expectetion count
//add some more checks
}

How to test business logic in method?

I have such method in my service layer:
public Long calculateItemsCostInShoppingCart(Long shoppingCartId) {
List<Item> items = shoppingCartRepository.findAllItems(shoppingCartId);
Long cost = 0L;
for (Item item : items) {
cost += item.getPrice();
}
return cost;
}
And I need to test calculation of summary cost of all items in list. I was thought about mockito, but it didn't work out cause mockito just create stubs, I need real entrance data and result based on them. How can do it?
// create mock
ShoppingRepository mock = mock(ShoppingRepository.class);
// define return value for method findAllItems()
when(mock.findAllItems()).thenReturn(listOf(...));
Here is an example how you can test it with Mockito:
public class SomeCalculatorTest {
#Mock
private ShoppingCartRepository shoppingCartRepository;
#InjectMocks
private SomeCalculator someCalculator = new SomeCalculator();
#Before
public void setUp() {
initMocks(this);
}
#Test
public void testEmptyItemsList() {
when(shoppingCartRepository.findAllItems(any())).thenReturn(new ArrayList<>());
final Long result = someCalculator.calculateItemsCostInShoppingCart(1L);
assertThat(result, is(0L));
}
#Test
public void testOneItemInList() {
when(shoppingCartRepository.findAllItems(any())).thenReturn(Arrays.asList(new ItemImpl(25L)));
final Long result = someCalculator.calculateItemsCostInShoppingCart(1L);
assertThat(result, is(25L));
}
#Test
public void testTwoItemInList() {
when(shoppingCartRepository.findAllItems(any())).thenReturn(Arrays.asList(new ItemImpl(25L), new ItemImpl(12L)));
final Long result = someCalculator.calculateItemsCostInShoppingCart(1L);
assertThat(result, is(37L));
}
}
Assuming that you are developing a Java web application which runs on a application server another option might be to use Arquillian (http://arquillian.org/). In a nutshell, Arquillian is a framework which allows you to test you logic in environment it will run. But it might be some work to integrate Arquillian into your project. We are using Arquillian in several projects and it works well so far. Even the Persistence module which is an Alpha version works well.
And I need to test calculation of summary cost of all items in list.
In this case, you have to isolate the shoppingCartRepository dependency that doesn't perform any calculation.
I need real entrance data and result based on them. How can do it?
It describes an integration test. In this case, don't use Mockito.
To unit test :
You have to mock the dependency and you also need a way to set it in the instance of the class under test.
A constructor is often a fine way (let calling the class under test MyService):
public MyService(ShoppingCartRepository shoppingCartRepository){
this.shoppingCartRepository = shoppingCartRepository;
}
Then, in the test you should mock ShoppingCartRepository, record a behavior for findAllItems() and pass the mock in the constructor of MyService.
#Mock
private ShoppingCartRepository shoppingCartRepositoryMock;
#Test
public void calculateItemsCostInShoppingCart(){
Long cartId = Long.valueOf(123);
// set the dependency
MyService myService = new MyService(shoppingCartRepositoryMock);
// create the mock
Mockito.when(shoppingCartRepositoryMock.findAllItems(cartId))
.thenReturn(createMockedItems());
//action
Long actualCost = myService.calculateItemsCostInShoppingCart(cartId);
// assertion
Assert.assertEquals(expectedCost, actualCost);
}
private List<Item> createMockedItems() { ... }
You can use Rest assured library for test
get Rest assured response Object, and call method for method object of list.
#BeforeClass
public static void init() {
RestAssured.baseURI = "http://localhost";
RestAssured.port = 8080;
}
#Test
public void testUserRegistration() {
Response response =
RestAssured
.given()
.get(URL_LOGIN)
.then()
.assertThat()
.statusCode(200);
Assert.assertThat(200, Matchers.is(200));
Assert.assertThat(response.getStatusCode(), Matchers.is(200));
}

how to unit test method using "Spring Data JPA" Specifications

I was playing with org.springframework.data.jpa.domain.Specifications, it's just a basic search :
public Optional<List<Article>> rechercheArticle(String code, String libelle) {
List<Article> result = null;
if(StringUtils.isNotEmpty(code) && StringUtils.isNotEmpty(libelle)){
result = articleRepository.findAll(Specifications.where(ArticleSpecifications.egaliteCode(code)).and(ArticleSpecifications.egaliteLibelle(libelle)));
}else{
if(StringUtils.isNotEmpty(code)){
result= articleRepository.findAll(Specifications.where(ArticleSpecifications.egaliteCode(code)));
}else{
result = articleRepository.findAll(Specifications.where(ArticleSpecifications.egaliteLibelle(libelle)));
}
}
if(result.isEmpty()){
return Optional.empty();
}else{
return Optional.of(result);
}
}
And that's actually working fine but I'd like to write unit tests for this method and I can't figure out how to check specifications passed to my articleRepository.findAll()
At the moment my unit test looks like :
#Test
public void rechercheArticle_okTousCriteres() throws FacturationServiceException {
String code = "code";
String libelle = "libelle";
List<Article> articles = new ArrayList<>();
Article a1 = new Article();
articles.add(a1);
Mockito.when(articleRepository.findAll(Mockito.any(Specifications.class))).thenReturn(articles);
Optional<List<Article>> result = articleManager.rechercheArticle(code, libelle);
Assert.assertTrue(result.isPresent());
//ArgumentCaptor<Specifications> argument = ArgumentCaptor.forClass(Specifications.class);
Mockito.verify(articleRepository).findAll(Specifications.where(ArticleSpecifications.egaliteCode(code)).and(ArticleSpecifications.egaliteLibelle(libelle)));
//argument.getValue().toPredicate(root, query, builder);
}
Any idea?
I was having almost the same problems as you had, and I changed my class that contains Specifications to be an object instead of just one class with static methods. This way I can easily mock it, use dependency injection to pass it, and test which methods were called (without using PowerMockito to mock static methods).
If you wanna do like I did, I recommend you to test the correctness of specifications with integration tests, and for the rest, just if the right method was called.
For example:
public class CdrSpecs {
public Specification<Cdr> calledBetween(LocalDateTime start, LocalDateTime end) {
return (root, query, cb) -> cb.between(root.get(Cdr_.callDate), start, end);
}
}
Then you have an integration test for this method, which will test whether the method is right or not:
#RunWith(SpringRunner.class)
#DataJpaTest
#Sql("/cdr-test-data.sql")
public class CdrIntegrationTest {
#Autowired
private CdrRepository cdrRepository;
private CdrSpecs specs = new CdrSpecs();
#Test
public void findByPeriod() throws Exception {
LocalDateTime today = LocalDateTime.now();
LocalDateTime firstDayOfMonth = today.with(TemporalAdjusters.firstDayOfMonth());
LocalDateTime lastDayOfMonth = today.with(TemporalAdjusters.lastDayOfMonth());
List<Cdr> cdrList = cdrRepository.findAll(specs.calledBetween(firstDayOfMonth, lastDayOfMonth));
assertThat(cdrList).isNotEmpty().hasSize(2);
}
And now when you wanna unit test other components, you can test like this, for example:
#RunWith(JUnit4.class)
public class CdrSearchServiceTest {
#Mock
private CdrSpecs specs;
#Mock
private CdrRepository repo;
private CdrSearchService searchService;
#Before
public void setUp() throws Exception {
initMocks(this);
searchService = new CdrSearchService(repo, specs);
}
#Test
public void testSearch() throws Exception {
// some code here that interact with searchService
verify(specs).calledBetween(any(LocalDateTime.class), any(LocalDateTime.class));
// and you can verify any other method of specs that should have been called
}
And of course, inside the Service you can still use the where and and static methods of Specifications class.
I hope this can help you.
If you are writing Unit Tests then you should probably mock the call to findAll() method of articleRepository Class using a mocking framework like Mockito or PowerMock.
There is a method verify() using which you can check if the mock is invoked for the particular parameters.
For Example, if you are mocking the findAll() method of articleRepository Class and want to know if this method is called with particular arguments then you can do something like:
Mokito.verify(mymock, Mockito.times(1)).findAll(/* Provide Arguments */);
This will fail the test if mock has not been called for the arguments that you provided.
Your problem is that you are doing too many things within that one method. You should have three different methods that work on articleRepository.
Then you can use mocking as the others suggest:
setup your mocks so that you know which call on articleRepository should be made
verify that exactly the expected calls are happening
Please note: these three methods should be internal; the main point there is: you can't test this method with ONE call from the outside; as it is doing more than one thing, depending on the input that you provide. Thus you need to create at least one test method for each of the potential paths in your code. And that becomes easier (from a conceptual point of view) when you separate your code into different methods.

Mock method as parameter another method

I have got a problem with testing how many times concrete methods (IFunction in the Operation instance) is invoked.
According to:
http://easymock.org/user-guide.html#mocking-annotations
http://www.ibm.com/developerworks/library/j-easymock/
How to use EasyMock expect
I wrote something as:
class Operation{
public double[] calculateSth(IFunction function, int [] t){
for(int i=0 ; i<5 ; i+=1)
function(t, new int[]{1,2,3});
return new double[]{1,2,3};
}
}
interface IFunction{
double f(int[] a, int[]b);
}
class ConcreteF implements IFunction{
double f(int[]a, int[]b){
return 5;
}
}
And my test class:
#TestSubject
Operation op;
#Mock
IFunction function;
#Before
public void setUp() throws Sth{
op=new Operation();
function = EasyMock.createMock(IFunction.class);
}
#Test
public void howManyTimes(){
EasyMock.expect(function.f(EasyMock.notNull(), EasyMock.notNull())
)
.andReturn((double)EasyMock.anyDouble()).times(3);
EasyMock.replay(function);
op.calculateSth(function, new double[]{0,0,0});
//verify
EasyMock.verify(function);
}
Result:
java.lang.NullPointerException
at org.easymock.internal.Injector.injectMocks(Injector.java:80)
at org.easymock.EasyMockSupport.injectMocks(EasyMockSupport.java:624)
at org.easymock.EasyMockRunner.withBefores(EasyMockRunner.java:50)
It's my first time using easymock and i don't know how to fix it ;/
I'll answer this question without going into the details of whether the original method does anything useful (the code doesn't even compile anyway), let alone the test method.
#TestSubject Operation op;
This line is a suspect. I realize that you are instantiating it in the #Before annotated setUp method, but it looks like the Easymock tries to inject the mocks (the ones annotated with #Mock) before it does anything (and understandably so) and blows up since the reference is null at that point.
The annotation support introduced in v3.2 is also seen as a way to eliminate the need for setUp method. But you seem to be mixing both and using it wrongly. Choose one or the other - I'd recommend you to use the annotations.
Quoting the Easymock user guide (this user guide is as good as it can ever be, so be sure read this up before using the library),
#RunWith(EasyMockRunner.class)
public class ExampleTest {
#TestSubject
private ClassUnderTest classUnderTest = new ClassUnderTest(); // 2
#Mock
private Collaborator mock; // 1
#Test
public void testRemoveNonExistingDocument() {
replay(mock);
classUnderTest.removeDocument("Does not exist");
}
}
The mock is instantiated by the runner at step 1. It is then set by
the runner, to the listener field on step 2. The setUp method can be
removed since all the initialization was done by the runner.

Categories

Resources