Background Info
I have a thread. It's a dedicated thread to continuously take out a task from a queue and write to a persistent repository. So it's code is like this.
public class Processor extends Thread {
//Context saves reference to the task queue and the write backEnd service
public GeneralProcessor(Context context){initProcessor( context, Type.GENERAL);}
public void run() {
...
synchronized (Processor.class) {
Thread curThread=currentThread();
Context context=getContext();
ConcurrentLinkedQueue<Task> taskQue =context.getTasks();
if (taskQue.size() > 0) {
Task t = taskQue.poll();
Transaction ts = new Transaction();
//all works to copy Task's member values to Transaction, and add other values to this transaction
//...
context.getService().save(ts);//this is the line I want to monitor. This is also related to issue in some way.
}
}
}
}
The Issue
But there is an issue when I wrote a unit test for this class. My original unit test is this.
#ExtendWith(MockitoExtension.class)
public class GeneralProcessorTest {
#InjectMocks
private GeneralProcessor generalProcessor;
#Mock
private Context context;
#Spy
private ConcurrentLinkedQueue<Task> tasks;
#Mock
private TransactionRepository transactionRepository;
#Captor
private ArgumentCaptor<Transaction> transactionArgumentCaptor;
#Mock
private TransactionService transactionService;
#BeforeEach
void setup() {
//transactionService=new TransactionServiceImpl(transactionRepository);
}
#Test
#SneakyThrows
void should_match_save_times_single_thread() {
//given
CountDownLatch latch=new CountDownLatch(0);
this.tasks.add(new Task(10));
//stub code
when(context.getTasks()).thenReturn(tasks);
when(context.getService()).thenReturn(transactionService);
//when
generalProcessor.start();
latch.await(1, TimeUnit.SECONDS);
//then
//the issue happened here!
verify(transactionService).save(transactionArgumentCaptor.capture());
List<Transaction> capturedArguments = transactionArgumentCaptor.getAllValues();
assertEquals(capturedArguments.size(),1);
}
But I got:
Wanted but not invoked:
transactionService.save(
<Capturing argument>
);
at com.example.demo.GeneralProcessorTest.should_match_save_times_single_thread(GeneralProcessorTest.java:65)
Actually, there were zero interactions with this mock.
In fact, I tried to init transactionService with new. But Mockito told me that in verify I can only use Mock object.
So I am confused. Is there any way to let me use verify while at the same time keep transactionService working as a normal object? Any info is appreciated.
Related
I am facing a strange problem while trying to unit test my code.
Here is my code :
public class ItemService {
private OfferService offerService;
#Inject
public ItemService (OfferService offerService){
this.offerService = offerService;
}
public List<Item> buildItems(ItemInfo itemInfo) {
List<Item> items = processItem(itemInfo);
Offers<Offer> offers = fetchItemInfo(items);
// based on the object offers, do some processing
}
private Offers<Offer> fetchItemInfo(List<Item> items) {
Offers<Offer> offers = new Offers<>();
// some processing here with offers.
// calling the db to fetch details
offerService.fetchInfoFromDB(offers);
return offers;
}
}
public class OfferService {
public void fetchInfoFromDB(Offers<Offer> offers) {
// fetching details from DB
// and updating the object **offers**
myDao.getDetailsById(id);
}
}
Now I have written junit to test the method buildItems()
UPDATE updating the mocks used and mock injection.
#RunWith(PowerMockRunner.class)
#PrepareForTest(ItemService.class)
public class ItemServiceTest{
#Mock private MyDAO myDao;
#Mock private OfferService offerService;
#Before
public void setUp() throws Exception {
ItemService itemService = new ItemService (offerService, myDao);
}
public void testBuildItems(){
// some code -----
itemInfo = buildItemInfo();
offerDetail = buildOfferDetail();
when(myDao.getDetailsById(Mockito.anyLong())).thenReturn(offerDetail);
// some code -----
// I need to implement some code which will actually call
// offerService.fetchInfoFromDB(offers);
// and update the --offers-- object and return it.
List<Item> items = itemService.buildItems(itemInfo);
Assert.assertNotNull(items);
}
}
I am running with coverage and I can see that the below line got executed but the actual method is not getting called :
offerService.fetchInfoFromDB(offers);
I am getting null values in offers. Then I added the below line :
doCallRealMethod().when(offerService).fetchInfoFromDB(offers);
Still the same result. The offers object is passed by reference and is getting updated after the DB call which I am mocking already. But upto that call my code is not reaching. How can I update the offers object in my junit. Please help.
Your test is calling a zero arg ItemService() constructor, not the one arg #Inject constructor you posted. Either your code won't compile, or you haven't actually shown us the code in question.
Also, you say you are mocking offerService:
You call when on myDao and not offerService,
you do not pass your mock offerService into your ItemService constructor, as in new ItemService(offerService), and
your doCallRealMethod won't work because your mock offerService won't use your mock myDao; you'll need to mock the call on offerService directly with a thenAnswer that changes the passed List<Offer>, as on my question you linked.
doAnswer(invocation -> {
((List<Offer>) invocation.getArgument(0)).add(offerDetail);
return null;
}).when(offerService).fetchInfoFromDb(any());
If you fix those three you will be considerably closer to a working test.
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
}
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));
}
We are using Project Reactor to run a particular operation asynchronously as per the code in ServiceTest below. To test this code, as per ServiceTest below, when setting up the Mono for the async operation we make the Mono pass it's result to a DirectProcessor with doOnNext that the test has access to, and then carry out our test call and assertions with StepVerifier.
The JavaDoc of StepVerifier#assertNext reads
Any AssertionErrors thrown by the consumer will be rethrown during verification.
We have found that is true only when the immediate scheduler (Schedulers.immediate()) is used and is not true when the single scheduler (Schedulers.single()) is used. When the single scheduler is used, AssertionErrors are not re-thrown, i.e. the test always passes.
Is it possible, and if so, how, to use the single scheduler and have AssertionErrors rethrown during verification as per the JavaDoc?
#Service
#RequiredArgsConstructor
public class Service implements WithReactive, WithTestProcessor<Response> {
#Getter
#Setter
private DirectProcessor<Response> processor = DirectProcessor.create();
#Setter
private Scheduler scheduler = Schedulers.single();
public void doAction() {
Mono.fromSupplier(this::doActionAsync)
.doOnNext(processor::onNext)
.subscribeOn(scheduler)
.subscribe();
}
private Response doActionAsync() {
...
}
...
}
public interface WithReactive {
void setScheduler(Scheduler scheduler);
}
public interface WithTestProcessor<T> {
void setProcessor(DirectProcessor<T> processor);
DirectProcessor<T> getProcessor();
}
#RunWith(SpringRunner.class)
#SpringBootTest
public class ServiceTest {
#Inject
private Collection<WithTestProcessor> withTestProcessors;
#Before
public void setTestProcessors() {
withTestProcessors.forEach(withTestProcessor -> withTestProcessor.setProcessor(DirectProcessor.create()));
}
#Inject
private Collection<WithReactive> withReactives;
#Before
public void makeReactiveSynchronous() {
withReactives.forEach(withReactive -> withReactive.setScheduler(Schedulers.immediate()));
}
#Test
private void test() {
StepVerifier.create(service.getProcessor())
.then(service::doAction)
.assertNext(response -> assertThat(logExtractor.getInsertsByTable("assets")).hasSize(1))
.thenCancel()
.verify();
}
}
This is a combination of three factors: the initial then, the fact that subscription happens in parallel of the verification due to subscribeOn and the thenCancel.
One workaround is to give enough time to the onNext to happen before the StepVerifier executes thenCancel, by putting a thenAwait(Duration.ofMillis(10)) before the thenCancel.
I'm using mockito with Spring Boot. I found the stubs not work when using the mocked object in a Runnable.
Here is the code sample:
#Component
public class TheClassIWantTest {
#Autowired
private ADependency aDependency;
#Autowired
private ThreadPoolTaskExecutor executor;
public void theMethodIWantTest {
executor.execute(new Runnable() {
#Override
public void run() {
Integer result = aDependency.doSomething(); // result should be 111, but it's null
}
})
}
}
The unit test:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = TestApplication.class)
#FixMethodOrder(value = MethodSorters.NAME_ASCENDING)
public class TheTest {
#Autowired
private TheClassIWantTest theClassIWantTest;
#MockBean
private ADependency aDependency;
#Test
public void testTheMethod() {
Ingeter testResult = 1;
when(aDependency.doSomething()).thenReturn(111); // this stub doesn't work...
theClassIWantTest.theMethodIWantTest();
}
}
I was expecting aDependency.doSomething() would return 111, because I defined the stub in the test case, but the result is null.
And if I remove the lines of
executor.execute(new Runnable() {
#Override
public void run() {
to turn the async runnable method to a normal sync method, the stub works good,e.g result == 111.
This makes me think the stubs don't work anymore in a runnable.
How can I fix this? Or is there a workaround?
Thanks!
Have you considered that your test may actually be running faster than your Runnable? The executor service is asked to run the Runnable code and will do that in a separate thread (taken from a ThreadPool). Meanwhile, your test class continues on the main thread. You may want to look at https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CountDownLatch.html that will allow you pause your test code until your business code has counted down on your latch. Note that you are modifying your business code to accommodate for testing. Unit testing multi-threaded code is never easy...