I have my database layer:
public class DataBaseLayer
{
public Result runQuery(Query q)
{
this.openSession();
this.runPackage(q);
Results r = this.fetchResults();
this.closeSession();
return r;
}
}
Currently all those methods are private methods.
But I want to be able to test them.
for example
private void testOpenSession_wrongUserNamePassword_returnsBadUserNamePassWordError();
private void testrunPackage_insufficientPrivileges_returnsInsufficientPrivlegesError();
The question is what's the nicest way of doing this?
I figure I could either:
Just test the runQuery method for bad username password etc.
Make those methods protected.
Make those methods public.
I assume your class is using some collaborator to connect to the database when you call this.openSession() mock that collaborator and have the mock return the responses desired for bad password or insufficient privileges so you can test the behaviour of your class in that scenario. eg
class under test
public class DataBaseLayer {
public DataBaseLayer(SomeDBClass dbObject){
this.dbObject = dbObject;
}
...
private void openSession() {
dbObject.connect(username, password);
}
...
public Result runQuery(Query q){
...
}
test class
public class DataBaseLayerTest {
#Test(expected = IncorrectPasswordException.class)
public void testOpenSession_wrongUserNamePassword_returnsBadUserNamePassWordError() {
SomeDBClass someDBClass = Mockito.mock(SomeDBClass.class)
Mockito.when(someDBClass.connect(Mockito.anyString(), Mockito.anyString())).throw(new IncorrectPasswordException())
DataBaseLayer underTest = new DataBaseLayer(someDBClass)
underTest.runQuery(someQuery);
}
}
Related
I have an abstract cache client with an implementation that I'm trying to add unit tests to, and it has a protected class implementation of the key. Like this:
public abstract class SimpleCacheClient<V extends Serializable> {
// Autowired RedissonClient and RedisKeyGenerator
public V get(SimpleCacheKey key) {
// return the cache entry from the autowired RedissonClient
}
public void set(SimpleCacheKey key, V value) {
// set the cache entry
}
public SimpleCacheKey getCacheKey(Object...keys) {
return new SimpleCacheKey(keyGenerator.generateKey(keys));
}
/**
* Simple wrapper for cache key to guarantee that implementations
* are using the key generator provided in this class
*/
protected class SimpleCacheKey {
private String key;
SimpleCacheKey(String key) {
this.key = key;
}
public String getKey() {
return key;
}
#Override
public String toString() {
return getKey();
}
}
}
And here's the implementation I'm trying to test:
public class CacheClientImplementation extends SimpleCacheClient<ArrayList<DateTime>> {
public void addEventDateTimes(String key, ArrayList<DateTime> eventDateTimes) {
// Do stuff with eventDateTimes and then
set(getCacheKey(key), eventDateTimes);
}
public ArrayList<DateTime> getEventDateTimes(String key) {
ArrayList<DateTime> eventDateTimes = get(getCacheKey(key));
// Do stuff with eventDateTimes.
return eventDateTimes;
}
}
I'm trying to test to make sure that CacheClientImplementation performs certain operations on the values provided to it before setting and getting.
I'm trying to mock the redis cache itself by hijacking the get() and set() methods to read and write from/to a HashMap so that I can check the contents of the "cache" in my tests.
#RunWith(MockitoJUnitRunner.class)
public class CacheClientImplementationTest{
#Mock
private RedissonClient redissonClient;
#Mock
private RedisKeyGenerator redisKeyGenerator;
#Spy
#InjectMocks
private CacheClientImplementation cacheClient = new CacheClientImplementation();
private final HashMap<String, ArrayList<DateTime>> cacheMap = new HashMap<>();
#Before
public void setup() {
Mockito.doAnswer((ver) -> {
cacheMap.put(ver.getArgumentAt(0, Object.class).toString(), ver.getArgumentAt(1, ArrayList.class));
return null;
}).when(cacheClient).set(Mockito.any(), Mockito.any(ArrayList.class));
Mockito.doAnswer((ver) -> cacheMap.getOrDefault(ver.getArgumentAt(0, Object.class).toString(), null))
.when(cacheClient).get(Mockito.any());
}
#After
public void teardown() {
cacheMap.clear();
}
}
However, I end up with this problem when I run a test in the file.
C:\...\CacheClientImplementationTest.java:20: error: SimpleCacheClient.SimpleCacheKey has protected access in SimpleCacheClient
}).when(cacheClient).set(Mockito.any(), Mockito.any(ArrayList.class));
Is there any way I can doAnswer for these methods without changing SimpleCacheKey?
Thanks!
This boils down to the visibility of the SimpleCacheKey class, you simply can't use it from a different package. So Mockito.any() can't use that class as a return type unless the unit test is in the same package as SimpleCacheClient.
One solution would be to move your unit test to the same package as SimpleCacheClient. If this is loaded from a different library that you can't change, you can re-create the same package structure to trick the compiler into thinking the package is the same, giving you access to protected classes.
But i believe this trick doesn't work with Java 9 modules.
A better solution would be to make a small modification to your CacheClientImplementation and unit test; encapsulate the part you can't influence and mock that part.
Since you don't really care about the SimpleCacheKey but just the String key, the following should work for your intentions:
public class CacheClientImplementation extends SimpleCacheClient<ArrayList<DateTime>> {
public void addEventDateTimes(String key, ArrayList<DateTime> eventDateTimes) {
// Do stuff with eventDateTimes and then
setForKey(key, eventDateTimes);
}
public ArrayList<DateTime> getEventDateTimes(String key) {
ArrayList<DateTime> eventDateTimes = getForKey(key);
// Do stuff with eventDateTimes.
return eventDateTimes;
}
protected ArrayList<DateTime> getForKey(String key) {
return super.get(getCacheKey(key));
}
protected void setForKey(String key, ArrayList<DateTime> value) {
super.set(getCacheKey(key), value);
}
}
And in the unit test you rewrite to the forKey variants we just created:
Mockito.doAnswer(myAnswer1()).when(cacheClient).setForKey(Mockito.any(), Mockito.any(ArrayList.class));
Mockito.doAnswer(myAnswer2()).when(cacheClient).getForKey(Mockito.any());
I've made the new methods protected as to not confuse callers which method to use, so in this case the unit test must be in same (test) package as the CacheClientImplementation.
I have a class for which I am writing a JUnit test. I am trying to test if a particular method is never called.
public class CountryProcess extends AbstractCountryProcess {
private static final Logger log = LoggerFactory.getLogger(CountryProcessor.class);
private static final Long MAX_FILE = 20l;
#Override
protected boolean processCountry(Region region, City city) {
Long maxFile = region.getRequiredLongValue(SIZE);
if (maxFile < MAX_FILE) {
cntDao.addCountryLandMark(city);
}
else {
log.warn("File size was big");
}
return true;
}
And the test class is:
public class CountryProcessTest {
#Rule
public final JUnitRuleMockery context = new JUnitRuleMockery();
private final CntDao cntDao = context.mock(CntDao.class);
#Before
public void setup() {
Injector injector = Guice.createInjector(new AbstractModule() {
#Override
protected void configure() {
bind(cntDao.class).toInstance(cntDao);
}
});
}
#Test
public void shouldIgnoreIfFileSizeBiggerThanPermitted() {
//some code to make it trigger ELSE statement above...
verify(cntDao, never()).addCountryLandMark(anyString());
}
}
But this returns the following error:
org.mockito.exceptions.misusing.NotAMockException:
Argument passed to verify() is of type $Proxy4 and is not a mock!
Make sure you place the parenthesis correctly!
See the examples of correct verifications:
verify(mock).someMethod();
verify(mock, times(10)).someMethod();
verify(mock, atLeastOnce()).someMethod();
Any idea how I can fix this in the current context. Please give an example using current code so I get a better idea?
You are mixing two mocking frameworks:
jMock - JUnitRuleMockery
Mockito - verify method
Clearly, they are not compatible with each other.
Your verify call looks ok, I believe it will work as soon as it receives a mock created with Mockito (Use Mockito.mock(CntDao.class))
As an alternative to never you can use Mockito.verifyNoMoreInteractions or Mockito.verifyZeroInteractions, but they are less specific.
In addition to the answer from #Lesiak, here is a reproducible example based on your code with both conditions tested and BDD implementation as well (commented out).
#ExtendWith(MockitoExtension.class)
class CountryProcessTest {
#Mock CountryDAO cntDao;
#Mock
Region region;
#Mock
City city;
#InjectMocks
CountryProcess countryProcess;
#Test
void processCountryLargeSize() {
// given
given(region.getRequiredLongValue()).willReturn(100L);
// when
countryProcess.processCountry(region, city);
// then
verifyNoInteractions(cntDao);
// then(cntDao).shouldHaveNoInteractions(); // BDD implementation
}
#Test
void processCountrySmallSize() {
// given
given(region.getRequiredLongValue()).willReturn(10L);
// when
countryProcess.processCountry(region, city);
// then
verify(cntDao).addCountryLandMark(city);
verifyNoMoreInteractions(cntDao);
// then(cntDao).should().addCountryLandMark(any()); // BDD implementation
// then(cntDao).shouldHaveNoMoreInteractions(); // BDD implementation
}
}
The rest of the classes here are provided for reference.
Region
public class Region {
private int size;
public Long getRequiredLongValue() {
return Integer.toUnsignedLong(size);
}
}
AbstractCountryProcess
public abstract class AbstractCountryProcess {
CountryDAO cntDao;
protected abstract boolean processCountry(Region region, City city);
}
I'm using easymock and powermock to write unit test case for the below isRegisteredUSer() of Class B. How to mock getUserInformation() of Class A and return a mocked UserAccessBean?
class A{
private int userId;
A(int userId){
this.userId = userId;
}
public UserAccessBean getUserInformation(){
UserAccessBean userAB = new USerAccessBean().findByUserId(userId);
return userAB;
}
}
Class B{
public static boolean isRegisteredUSer(int userId){
A a = new A(userId);
UserAccessBean userAB = a.getUserInformation();
if(userAB.getUserType().equals("R")){
return true;
}
return false;
}
JUnit
public class BTest extends EasyMockSupport{
UserAccessBean userAB = null;
A a = null;
int userId = 12345;
#Before
public void setUp() throws Exception {
userAB = new UserAccessBean();
}
#Test
public void when_UserDesctiptionIsR_Expect_True_FromIsRegisteredUser() throws Exception{
//data setup
userAB.setDescription("R");
A a = new A(12345);
EasyMock.expect(a.isRegisteredUser()).andReturn(userAB);
PowerMock.replayAll();
Boolean flag = B.isRegisteredUser(userId);
assertEquals(flag, true);
PowerMock.verifyAll();
}
}
Even If I use EasyMock.expect() to mock getUserInformation() method call, my console is going inside getUserInformation() when I run my JUnit.
Can someone please help me to mock another class functions method (Class A's getUserInformation) call from the method (Class B's isRegisteredUSer) being tested?
Please, next time copy actual working code. Your code has many typos and anomalies that makes it hard to workaround.
Nevertheless, I think you want a normal EasyMock for A and a mock on new for B. The code below should answer your question
#RunWith(PowerMockRunner.class)
#PrepareForTest({A.class, B.class})
public class BTest extends EasyMockSupport {
UserAccessBean userAB = new UserAccessBean();
A a;
int userId = 12345;
#Test
public void when_UserDesctiptionIsR_Expect_True_FromIsRegisteredUser() throws Exception {
//data setup
userAB.setDescription("R");
A a = createMock(A.class);
expect(a.getUserInformation()).andReturn(userAB);
replayAll();
PowerMock.expectNew(A.class, userId).andReturn(a);
PowerMock.replay(A.class);
Boolean flag = B.isRegisteredUser(userId);
assertEquals(flag, true);
PowerMock.verifyAll();
verifyAll();
}
}
I will however highly recommend A to be injected into B and to get rid of the static method. That will get rid of PowerMock and simplify the code.
I have a tricky situation. I am using MVP architecture for android but thats not important. I have a class called DoStandardLoginUsecase that basically just connects to a server with login info and gets a access token. i am trying to test it. But the problem is the context that i am passing in to it so i can initialize dagger.
public class DoStandardLoginUsecase extends BaseUseCase {
#Inject
UserDataRepository mUserDataRepo;
private StandardLoginInfo loginInfo;
public DoStandardLoginUsecase(Context context) {
/* SEE HERE I AM USING A APPLICATION CONTEXT THAT I PASS TO DAGGER
*/
((MyApplication)context).getPresenterComponent().inject(this);
}
#Override
public Observable<Login> buildUseCaseObservable() {
return mUserDataRepo.doStandardLogin(loginInfo);
}
public void setLoginInfo(StandardLoginInfo loginInfo) {
this.loginInfo = loginInfo;
}
}
and here is the test i have so far:
public class DoStandardLoginUsecaseTest {
DoStandardLoginUsecase standardLoginUsecase;
StandardLoginInfo fakeLoginInfo;
TestObserver<Login> subscriber;
MockContext context;
#Before
public void setUp() throws Exception {
//now when i create the object since its a mock context it will fail when it tries to call real things as these are stubs. So how do i test this object. how do i create an instance of this object ? I am willing to use [daggerMock][1] if that helps also.
standardLoginUsecase = New DoStandardLoginUsecase(context);
fakeLoginInfo = new StandardLoginInfo("fred#hotmail.com","Asdfgh4534");
subscriber = TestObserver.create();
}
#Test
public void buildUseCaseObservable(){
standardLoginUsecase.seLoginInfo(fakeLoginInfo);
standardLoginUsecase.buildUseCaseObservable().subscribe(subscriber);
subscriber.assertNoErrors();
subscriber.assertSubscribed();
subscriber.assertComplete();
}
}
I would do the test like this:
public class DoStandardLoginUsecaseTest {
private DoStandardLoginUsecase target;
private MyApplication contextMock;
#Before
public void beforeEach() {
contextMock = Mockito.mock(MyApplication.class);
// Note that you need to mock the getPresenterComponent
// but I don't know what it returns.
target = new DoStandardLoginUsecase(contextMock);
}
#Test
public void buildUseCaseObservable() {
UserDataRepository userDataMock = Mockito.mock(UserDataRepository.class);
StandardLoginInfo loginInfoMock = Mockito.mock(StandardLoginInfo.class);
target.mUserDataRepo = userDataMock;
target.setLoginInfo(loginInfoMock);
Observable<Login> expected = // create your expected test data however you like...
Mockito.when(userDataMock.doStandardLogin(loginInfoMock)).thenReturn(expected);
Observable<Login> actual = target.buildUseCaseObservable();
Assert.areSame(actual, expected);
}
}
I have been trying to run the following test using mockito and junit and I keep on getting "java.lang.NullPointerException: name must not be null"
Can anyone tell me why this is happening?
On debugging, I found out that this exception is thrown when the test executes the following statement in isStopValid(String) method:
FacilityValidationUtil facUtil = new FacilityValidationUtil();
#RunWith(MockitoJUnitRunner.class)
public class MyFormTest{
#InjectMocks MyForm form = new MyForm();
#Mock FacilityValidationUtil facUtil;
#Test
public void testIsStopValid() throws FinderException{
when(facUtil.isFacilityValid("")).thenReturn(false);
form.setOrigin("");
assertEquals(false, form.isStopValid(form.getOrigin()));
}
}
Class with method to be tested:
public class MyForm{
FacilityValidationUtil facUtil = new FacilityValidationUtil();
public boolean isStopValid(String stop){
try {
return facUtil.isFacilityValid(stop);
} catch (FinderException e) {
log.error("Error finding the stop. "+e.getCause());
return false;
}
}
}
public class FacilityValidationUtil{
private FacilityDAO facilityDao = new HibernateFacilityDAO();
public boolean isFacilityValid(String facility) throws FinderException{
boolean test;
FacilityImpl facilityImpl = facilityDao.findFacilityByNassCode(facility);
test = (facilityImpl==null)?false : true;
return test;
}
}
public class HibernateFacilityDAO extends HibernateAbstractDeltaDAO implements FacilityDAO {
public HibernateFacilityDAO() {
super(false);
}
}
Short Answer: You are trying to mock a variable (facUtil) that is local to your isStopValid method, so the mock version of this object in your test is never going to be called because you are 'newing it up" each time.
Long Answer: It looks like you are trying to mock the call to your FacilityValidationUtil class, and if this is the case, then you need to either make the class a field so that Mockito can inject the object by reflection (if this object is thread safe, which it looks like it is) or explore a mocking framework like PowerMockito that will allow you to mock a constructor (google for PowerMockito when new).
PowerMockito.whenNew(FacilityValidationUtil.class).withNoArguments().thenReturn(facUtil);
Mockito doesn't support any mocking of constructor args by default.
EDIT
If you are still having trouble, then I would suggest starting with a smaller example. I've put together one for you that works and uses the code you are trying to test (It's using inner classes though, which Mockito has some quirky rules about, but I'm just doing it to compress the example).
#RunWith(MockitoJUnitRunner.class)
public class MyFormTest {
#InjectMocks
private MyForm form = new MyForm();
#Mock
private FacilityValidationUtil facUtil;
#Test
public void testIsStopValid_false() {
when(facUtil.isFacilityValid("")).thenReturn(false);
assertEquals(false, form.isStopValid(""));
}
#Test
public void testIsStopValid_true() {
when(facUtil.isFacilityValid("")).thenReturn(true);
assertEquals(true, form.isStopValid(""));
}
public class MyForm {
private FacilityValidationUtil facUtil = new FacilityValidationUtil();
public boolean isStopValid(String stop) {
try {
return facUtil.isFacilityValid(stop);
} catch (FinderException e) {
return false;
}
}
}
public class FacilityValidationUtil {
public boolean isFacilityValid(String facility) throws FinderException {
throw new RuntimeException(facility);
}
}
public class FinderException extends RuntimeException {
public FinderException(String message) {
super(message);
}
}
}
What's really important is that your mock is not getting injected correctly. Until you get that resolved, you are going to keep getting the same error. Set a break point in your MyForm at the point you call facUtil.isFaciltyValid and look at the object. It should be a mockito object, not your class.
Good luck.