74

I'm using Mockito 1.9.0. How would i verify that a method got called exactly once, and that one of the fields passed to it contained a certain value? In my JUnit test, I have

@Before
public void setupMainProg() { 
    // Initialize m_orderSvc, m_opportunitySvc, m_myprojectOrgSvc
    ...
    m_prog = new ProcessOrdersWorker(m_orderSvc, m_opportunitySvc, m_myprojectOrgSvc);
}   // setupMainProg

@Test
public void testItAll() throws GeneralSecurityException, IOException { 
    m_prog.work();  
}

The method "work" calls a method of "m_orderSvc" (one of the arguments passed in to the object). "m_orderSvc," in turn contains a member field, "m_contractsDao". I want to verify that "m_contractsDao.save" got called exactly once and that the argument passed to it contains a certain value.

This may be a little confusing. Let me know how I can clarify my question and I'm happy to do so.

bric3
  • 40,072
  • 9
  • 91
  • 111
Dave
  • 15,639
  • 133
  • 442
  • 830

3 Answers3

75

First you need to create a mock m_contractsDao and set it up. Assuming that the class is ContractsDao:

ContractsDao mock_contractsDao = mock(ContractsDao.class);
when(mock_contractsDao.save(any(String.class))).thenReturn("Some result");

Then inject the mock into m_orderSvc and call your method.

m_orderSvc.m_contractsDao = mock_contractsDao;
m_prog = new ProcessOrdersWorker(m_orderSvc, m_opportunitySvc, m_myprojectOrgSvc);
m_prog.work(); 

Finally, verify that the mock was called properly:

verify(mock_contractsDao, times(1)).save("Parameter I'm expecting");
mamboking
  • 4,559
  • 23
  • 27
  • 10
    FYI, you could leave off `, times(1)`, as it is always implied unless you add a quantifier specifying something other than exactly one-time. And instead of `any(String.class)`, there is a slightly more convenient `anyString()`. – Kevin Welker Aug 03 '12 at 21:08
  • 3
    It's also worth noting that the argument that's passed to the method AFTER `verify` is compared using `equals` to the argument that was passed during the actual test. So, whatever the method is (the `save` method in mamboking's example), think about the _type_ of each parameter, and whether a comparison with `equals` is what you actually want. If you want the argument to be tested with something other than `equals`, you'll need an `ArgumentMatcher` of some kind (which might be an `ArgumentCaptor` as in Kevin Welker's answer). – Dawood ibn Kareem Aug 05 '12 at 05:45
  • How do you specify exactly once, not two or more? @KevinWelker's comment says it's implicit, but not sure if it means exactly once, or at least once. – aliteralmind Jun 19 '15 at 18:53
  • if you leave off the optional VerificationMode argument (i.e., `times(1)`), the ALWAYS implied default VerificationMode is `times(1)`. If you want or need to test for "atLeast" or something similar, you can explicitly add VerificationMode of `atLeastOnce()`, `atLeast(n)`, or `atMost(n)`. But since `times(1)` is the most frequently used, the overloaded `verify()` has a form that does not require an explicit VerificationMode and uses `times(1)` by default. – Kevin Welker Jun 19 '15 at 20:41
  • 1
    Rather than passing the exact argument that I expect, is there a way to instead pass a predicate over possible arguments that could be passed to the method? E.g. `verify(mock_contractsDao, times(1)).save((String s) -> s.length() == 23);`? – jameshfisher Aug 14 '15 at 10:33
  • @jamesfisher The method signature of `save(String s)` precludes you from passing a `Predicate` instead of a String. However, to accomplish what you want you can instead create a Mockito `ArgumentCaptor` and then write `verify(mock_contractsDao).save(myMock.capture());` which adheres to the contract. Then after the verify, you can `assertEquals(23, myMock.getValue());` It would be interesting enhancement to ArgumentCaptor to be able to create one that took a Predicate that did the asserting at the same time as the verification. – Kevin Welker Mar 14 '16 at 14:47
  • 1
    Also I haven't tried anything in Mockito 2.x yet, but you might find what you want in the new ArgumentMatcher. This syntax may not be right, but the following may be close to what you want under Mockito 2.x: `verify(mock_contractsDao).save(argThat(s->s.length==23));` – Kevin Welker Mar 14 '16 at 14:58
  • you need to use eq("some result") instead if you have matchers for other arguments. either all matchers or all literals. cant mix and match. – Amir Ziarati Apr 21 '22 at 21:22
30

Building off of Mamboking's answer:

ContractsDao mock_contractsDao = mock(ContractsDao.class);
when(mock_contractsDao.save(anyString())).thenReturn("Some result");

m_orderSvc.m_contractsDao = mock_contractsDao;
m_prog = new ProcessOrdersWorker(m_orderSvc, m_opportunitySvc, m_myprojectOrgSvc);
m_prog.work(); 

Addressing your request to verify whether the argument contains a certain value, I could assume you mean that the argument is a String and you want to test whether the String argument contains a substring. For this you could do:

ArgumentCaptor<String> savedCaptor = ArgumentCaptor.forClass(String.class);
verify(mock_contractsDao).save(savedCaptor.capture());
assertTrue(savedCaptor.getValue().contains("substring I want to find");

If that assumption was wrong, and the argument to save() is a collection of some kind, it would be only slightly different:

ArgumentCaptor<Collection<MyType>> savedCaptor = ArgumentCaptor.forClass(Collection.class);
verify(mock_contractsDao).save(savedCaptor.capture());
assertTrue(savedCaptor.getValue().contains(someMyTypeElementToFindInCollection);

You might also check into ArgumentMatchers, if you know how to use Hamcrest matchers.

Jaymes Bearden
  • 2,009
  • 2
  • 21
  • 24
Kevin Welker
  • 7,719
  • 1
  • 40
  • 56
20

This is the better solution:

verify(mock_contractsDao, times(1)).save(Mockito.eq("Parameter I'm expecting"));
thug-gamer
  • 355
  • 6
  • 9
  • This is better when you are dealing with non primitive types. – Pikachu Mar 03 '17 at 22:37
  • this works too also you have to use eq() if you have other arguments that are matchers like any(), anyOrNull() etc. you can use matchers and literals at the same time. – Amir Ziarati Apr 21 '22 at 21:23