How to test exceptions with assertThrows - unit-testing

I have a function called "foo" which produces an ArrayList of strings. foo always returns a non-null array of size 2. When I try to reach an element which is out of the scope, I should get "IndexOutOfBounds" exception. There are two variations of the test method. The second one fails while the first one works just fine. What do you think is causing the second one to fail?
I've tried checking the exception using assertThrows but it didn't work as desired.
import java.util.ArrayList;
public class fooClass {
static ArrayList<String> foo (){
ArrayList<String> arrayOfSize2 = new ArrayList<String>();
arrayOfSize2.add("x");
arrayOfSize2.add("y");
return arrayOfSize2;
}
}
The following test chunk is working
#Test
void testFoo() {
ArrayList<String> dummyArrayList = FooClass.foo();
assertEquals("x",dummyArrayList.get(0));
assertEquals("y",dummyArrayList.get(1));
assertThrows(IndexOutOfBoundsException.class, () -> dummyArrayList.get(2));
}
But this one is not working
#Test
void testFoo() {
ArrayList<String> dummyArrayList = new ArrayList<String>();
dummyArrayList=fooClass.foo();
assertEquals("x", dummyArrayList.get(0));
assertEquals("y", dummyArrayList.get(1));
assertThrows(IndexOutOfBoundsException.class, () -> dummyArrayList.get(2));
}
I expect the output of assertThrows to be True for the second code as well but the actual output is Local variable dummyArrayList defined in an enclosing scope must be final or effectively final

I think your approach is wrong, as it will also terminate exceptionally in the off-chance the foo() method returns a list of less than 2 elements, or null.
I'd test it in this order:
#Test
void testFoo() {
ArrayList<String> dummyArrayList = FooClass.foo();
assertNotNull(dummyArrayList);
assertEquals(2,dummyArrayList.size());
assertEquals("x", dummyArrayList.get(0));
assertEquals("y", dummyArrayList.get(1));
}
[edit] or even simpler:
#Test
void testFoo() {
ArrayList<String> dummyArrayList = FooClass.foo();
assertIterableEquals(Arrays.asList("x", "y"), dummyArrayList);
}

Variables used in lambda expression should be effectively final. the assertion is fine but the lambda is complaining

Related

BDD Mockito - alias for verify(...) when using argument captor?

I wrote the test which uses BBDMockito and Argument Captor. Argument Captor is used only to capture callback and invoke onDataNotAvailable(), not for the verification.
#Test
public void loadNoItemFromRepository_showsMissingItem() {
//given
itemDetailPresenter = new ItemDetailPresenter(UNCHECKED_ITEM.getId(), itemsRepository, itemDetailView);
given(itemDetailView.isActive()).willReturn(true);
//when
itemDetailPresenter.load();
verify(itemsRepository).getItem(eq(UNCHECKED_ITEM.getId()), getItemCallbackArgumentCaptor.capture());
getItemCallbackArgumentCaptor.getValue().onDataNotAvailable();
//then
then(itemDetailView).should().showMissingItem();
}
Verify placed in //when section is confusing because the name suggests it should be placed in the verification section (//then). Is there an alias for verify() so I can use it with argument captor and the name will be more appropriate for //when?
Edited:
The code which I want to test is if itemDetailView.showMissingItem() was called.
public void load() {
(...)
itemsRepository.getItem(itemId, new ItemsDataSource.GetItemCallback() {
#Override
public void onItemLoaded(Item item) {
(...)
if (nonNull(item))
showItem(item);
else
itemDetailView.showMissingItem();
}
#Override
public void onDataNotAvailable() {
(...)
itemDetailView.showMissingItem();
}
});
}
verify(...) is an assertion, it's used to check a method was/wasn't called and how many times - it belongs in the //then section
I see you're also using an argumentCaptor but aren't checking anything with it, the proper process there would be to assert that the captured value (getItemCallbackArgumentCapture.getValue() contains/equals a value you expect).

JUnit 5: Assert an exception is thrown fails with an error

I am working on writing unit test cases for my DAO using the Junit5. I want to test whether the BadRequestException is thrown when maximum template reached and check whether NotFoundException is thrown when template is not found. But I am getting the following exception:
org.mockito.exceptions.misusing.InvalidUseOfMatchersException: You cannot use argument matchers outside of verification or stubbing.
My code:
TemplateDao.java:
#Transactional(readOnly = true)
public MyTemplateDetailsView getTemplateDetails(Long templateId) {
Session session = sessionFactory.getCurrentSession();
MyTemplate myTemplate = getTemplate(templateId);
if (null == myTemplate || myTemplate.getStatus() != TemplateStatus.ACTIVE.value) {
throw new NotFoundException("template.not.found");
}
Integer templatesCount = getTempletsCount(templateId);
if (templatesCount > templateLimit) {
LOGGER.info("max template count reacged");
throw new BadRequestException("template.limit.exceded");
}
}
TemplateDaoTest.java:
#Test
void getTemplateDetails_ThrowsNotFoundException_IfTemplateNotinActiveStatus() {
Assertions.assertThrows(NotFoundException.class, () -> {
when(templateDao.getTemplate(anyLong())).thenReturn(null);
});
}
#Test
public void getTemplateDetails_ThrowsBadRequestException_IfTemplateLimitExceeded() {
Integer limit = 100;
assertThrows(BadRequestException.class, () -> {
when(templateDao.getTempletsCount(anyLong())).thenReturn(limit);
templateDao.getTemplateDetails(anyLong);
});
}
I suspect the problem is in the following block of code:
assertThrows(BadRequestException.class, () -> {
when(templateDao.getTempletsCount(anyLong())).thenReturn(limit);
templateDao.getTemplateDetails(anyLong);
});
I'm not sure where the anyLong comes from, so I'll make an assumption that you meant anyLong(), as in org.mockito.ArgumentMatchers.anyLong.
If so, the problem with this is that you're trying to use a Matcher instead of a concrete value outside of stubbing context (i.e. outside of the specification of what method should return when called).
You need to pass in a proper Long, i.e. 150L (or whatever is larger than templateLimit) when you're calling your stubbed method. For example:
assertThrows(BadRequestException.class, () -> {
when(templateDao.getTempletsCount(anyLong())).thenReturn(limit);
templateDao.getTemplateDetails(150L); // note the concrete value
});
It's actually fairly trivial to reproduce if you're inclined to do so (I'm using JUnit 4, but it should be okay with JUnit 5, too):
public class MatchersTest {
#Test
public void testConversion() {
Converter converter = new Converter();
when(converter.from(anyLong())).thenReturn("1");
converter.from(anyLong());
}
private static class Converter {
String from(Long number) {
return String.valueOf(number);
}
}
}

Mockito - how to mock/verify a method call which accepts a new object?

I have a method (method1) that I'd like to test, which based on parameters provided creates an object and calls another method (method2). So I'm mocking method2, which accepts an object (sampleObj).
public void method1(booleanParam) {
if(booleanParam){
List<SampleObj> fooList = new ArrayList<SampleObj>;
fooList.add(new SampleObj("another param"));
anotherService.method2(fooList);
}
//some other smart logic here
}
And here's my test with same obfuscated names (sorry if I missed any typo):
public void testMethod1() {
AnotherService mockedAnotherService = PowerMockito.mock(AnotherService.class);
ServicesFactory.getInstance().setMock(AnotherService.class, mockedAnotherService);
List<SampleObj> fooList = new ArrayList<SampleObj>;
fooList.add(new SampleObj("another param"));
// assert and verify
service.method1(true);
Mockito.verify(mockedAnotherService, times(1)).method2(fooList);
}
The problem is, when I try to mock the anotherService, I need to pass an object to method2, so I have to create a new one. But since it's a new object, it's not the same object, which will be passed from inside the method1, hence the test fails with the exception:
Argument(s) are different! Wanted:
anotherService.method2(
[com.smart.company.SampleObj#19c59e46]
);
-> at <test filename and line # here>
Actual invocation has different arguments:
anotherService.method2(
[com.smart.company.SampleObj#7d1a12e1]
);
-> at <service filename and line # here>
Any ideas how to accomplish that?
You have a few options:
Implement equals and hashCode on SampleObj. Because you didn't wrap fooList in a matcher, Mockito checks with List.equals, which checks equals for corresponding objects in each List. The default behavior of Object.equals is that a.equals(b) iff a == b--that is, objects are equal iff they refer to the same instance--but you're welcome to override that if every SampleObj("foobar") equals every other SampleObj("foobar").
Use a Hamcrest Matcher you write.
private static Matcher<List<SampleObj>> isAListWithObjs(String... strings) {
return new AbstractMatcher<List<SampleObj>>() {
#Override public boolean matches(Object object) {
// return true if object is a list of SampleObj corresponding to strings
}
};
}
// in your test
verify(mockedAnotherService).method2(argThat(isAnObjListWith("another param")));
Note that you could also just make a Matcher of a single SampleObj, and then use a Hamcrest wrapper like hasItem. See more matchers here.
Use a Captor to check equals your own way:
public class YourTest {
// Populated with MockitoAnnotations.initMocks(this).
// You can also use ArgumentCaptor.forClass(...), but with generics trouble.
#Captor ArgumentCaptor<List<SampleObj>> sampleObjListCaptor;
#Test public void testMethod1() {
// ...
verify(mockedAnotherService).method2(sampleObjListCaptor.capture());
List<SampleObj> sampleObjList = sampleObjListCaptor.getValue();
assertEquals(1, sampleObjList.size());
assertEquals("another param", sampleObjList.get(0).getTitle());
}

Moq - how to verify method call which parameter has been cleaned (a list)

I've got the following code and I need help to write a unit test for it. I'm using Moq library.
Here's the deal. I have a business class with a dependency to a repository (interface), so I can use it to save my entities to the database. My entity is basically a list of strings. The method AddAndSave, from MyBusinessClass, grab the value it receives as a parameters, put it into the list and call Save method from IRepository. Then, I clear the list of my entity. The code below show this example (I've made it simple so I can explain it here).
There's a unit test, too.
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
namespace TestesGerais
{
public class MyEntity
{
public MyEntity()
{
MyList = new List<string>();
}
public List<string> MyList { get; set; }
}
public interface IRepository
{
void Save(MyEntity entity);
}
public class MyBusinessClass
{
public IRepository Repository { get; set; }
private MyEntity _entity = new MyEntity();
public void AddAndSave(string info)
{
_entity.MyList.Add(info);
Repository.Save(_entity);
_entity.MyList.Clear(); // for some reason I need to clear it
}
}
[TestClass]
public class UnitTest10
{
[TestMethod]
public void TestMethod1()
{
var mock = new Mock<IRepository>();
MyBusinessClass b = new MyBusinessClass() { Repository = mock.Object };
b.AddAndSave("xpto");
mock.Verify(m => m.Save(It.Is<MyEntity>(x => x.MyList[0] == "xpto")), Times.Exactly(1));
}
}
}
My unit-test check if the IRepository's Save method was called with its parameter (an entity) having one element in the list, and having the value "xpto" in this element.
When I run this test, it turns red with the error message "Test method TestesGerais.UnitTest10.TestMethod1 threw exception:
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index".
Ok, this is caused by the list that has been cleaned. If I comment the line "_entity.MyList.Clear();", everything goes well.
My question is: how can I test this without commenting the "Clear" line in my business class, and making sure that my repository's method is called passing the specific value (entity with one element with value "xpto")?
Thanks
I've changed my unit test using the Callback feature of Moq. This way, I can setup the mock so when AddAndSave is called, the parameter it receives is saved into a variable from my unit test, and I can assert it later.
[TestMethod]
public void TestMethod1()
{
var mock = new Mock<IRepository>();
string result = string.Empty;
mock.Setup(m => m.Save(It.IsAny<MyEntity>())).Callback((MyEntity e) => { result = e.MyList[0]; });
MyBusinessClass b = new MyBusinessClass() { Repository = mock.Object };
b.AddAndSave("xpto");
Assert.AreEqual(result, "xpto");
}
You could split your method up a bit. "AddAndSave" isn't all it does. You could then just test the behaviour of the adding and saving bit in isolation.

Moq tests using ExpectSet() with It.Is<T>() aren't behaving as... expected

I've isolated the behaviour into the following test case. I'd be grateful to anyone who can tell me how to expect/verify a property set for a List<T> property - it appears there's something going on inside It.Is<T>(predicate) that isn't making a whole lot of sense to me right now. Sample code will run as a console app from VS2008 - you'll need to add a reference to Moq 2.6 (I'm on 2.6.1014.1) - please try uncommenting the different ExpectSet statements to see what's happening...
using System;
using Moq;
using System.Collections.Generic;
namespace MoqDemo {
public interface IView {
List<string> Names { get; set; }
}
public class Controller {
private IView view;
public Controller(IView view) {
this.view = view;
}
public void PopulateView() {
List<string> names = new List<string>() { "Hugh", "Pugh", "Barney McGrew" };
view.Names = names;
}
public class MyApp {
public static void Main() {
Mock<IView> mockView = new Mock<IView>();
// This works - and the expectation is verifiable.
mockView.ExpectSet(mv => mv.Names);
// None of the following can be verified.
// mockView.ExpectSet(mv => mv.Names, It.Is<Object>(o => o != null));
// mockView.ExpectSet(mv => mv.Names, It.Is<List<string>>(names => names.Count == 3));
// mockView.ExpectSet(mv => mv.Names, It.IsAny<IList<String>>());
Controller controller = new Controller(mockView.Object);
controller.PopulateView();
try {
mockView.VerifyAll();
Console.WriteLine("Verified OK!");
} catch (MockException ex) {
Console.WriteLine("Verification failed!");
Console.WriteLine(ex.Message);
}
Console.ReadKey(false);
}
}
}
}
I'm not using the very latest version of Moq, so I don't have an overload of ExpectSet that takes two parameters, but I've had some success with this pattern:
mockView.ExpectSet(mv => mv.Names).Callback(n => Assert.That(n != null));
The Assert (from NUnit) call in the callback will throw an exception if the value assigned to .Names doesn't match the predicate. It does make it hard to trace when a test fails, though. I agree that the ability to pass an It.Is or It.IsAny as the second parameter would be handy.
The second parameter of ExpectSet() is the value you're expecting. You can't use It.Is<T> in this case as there's no overload that takes a predicate - though it would be nice ;) Here's a (simplified) excerpt from your sample, illustrating the use of a value:
var mockView = new Mock<IView>();
var list = new List<string> { "Hugh", "Pugh", "Barney McGrew" };
mockView.ExpectSet(mv => mv.Names, list);
mockView.Object.Names = list;
Hope that helps.
Edit: fixed typo.
BTW, It.Is is not supported on ExpectSet. Your code compiles just because they are regular method invocations when used as values (as opposed to expressions), whereas when used in an Expect expression they are pre-processed by Moq and given specific meaning (rather than the null/default value that all It.Is members actually return).
You could use the stub behavior on the given property (mockView.Stub(mv => mv.Names)) and later assert directly for its value after execution.
Moq doesn't provide an overload receiving It.IsAny as it's effectively the same as calling ExpectSet without passing an expected value ;)