简体   繁体   中英

Java Mockit : How to Mock a generic class method in JMockit

Hi I have the following classes

public class DataAccessLayer<T> {
  public T getData(Class<?> dataInfoType ,Integer id){
  //Some logic here
  } 
}

public class ServiceLayer{
    //this method has to be tested
     public Integer testingMethode{
         //The following line should be mocked
         UtilClass info =  new DataAccessLayer<UtilClass>().getData(UtilClass.class, 1); 
        retutn info.getSomeFieldWithIntegerValue();
     }
 }

I want to write test cases for testingMethode for that I need to mock the getData() method in DataAccessLayer<T>

Is it possible with jmockit to mock a Template(Generic ) class?

In JMockit there's actually no need to create a holding variable in the ServiceLayer class, nor is there a need to make a parameterized subclass of your DataLayer . The following test works just fine:

package com.example.dsohl;

import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;

import org.junit.Test;
import org.junit.runner.RunWith;

import mockit.Deencapsulation;
import mockit.Expectations;
import mockit.Mocked;
import mockit.Tested;
import mockit.integration.junit4.JMockit;

@RunWith(JMockit.class)
public class TestTest {

    public static class UtilClass {
        public Integer foo() {
            return 5;
        }
    }

    public static class DataLayer<T> {
        public T getItem(Class<T> clazz, int itemId) {
            return null;
        }
    }

    public static class ServiceLayer {
        public Integer testMethod() {
            UtilClass util = new DataLayer<UtilClass>().getItem(UtilClass.class, 1);
            return util.foo();
        }
    }

// Test really begins here
    @Tested ServiceLayer svc;
    @Mocked DataLayer<UtilClass> data;
    @Mocked UtilClass util;

    @Test
    public void testDateSubtraction() throws Exception {
        new Expectations() {
            {
                new DataLayer<UtilClass>(); result = data;
                onInstance(data).getItem(UtilClass.class, 1); result = util;
                util.foo(); result = 37;
            }
        };

        Integer i = svc.testMethod();
        assertThat(i, equalTo(37));
    }

}

A few notes: First, my DataLayer.getItem() returns null so if the injection fails, we get a NullPointerException , nice and obvious. Obviously your code won't work like this; this is only to convince you.

Second, I use onInstance() so that we can be 100% certain that the result of the DataLayer constructor is what we are using in the next steps of the test. The default behaviour of Expectations on a @Mocked object is to record the expectation against any object of that class; this is how we are certain that it's our object that's being used. (Ordinarily I don't worry myself about this, but when using new I like to be certain.)

Finally, I'm omitting some other stuff I might do in cases like this, like use a Verifications block, etc. Just trying to be as straightforward as possible.

Enjoy!

A generic class can be mocked the same way a non-generic one:

@Test
public void example(@Mocked final DataAccessLayer<UtilClass> mock)
{
    final UtilClass data = new UtilClass(123);
    new Expectations() {{ mock.getData(UtilClass.class, 1); result = data; }};

    int result = new ServiceLayer().testingMethode();

    assertEquals(123, result);
}

(I can only really answer for Mockito, as that is what I am most familiar with; but the same principle should be applicable in other mocking frameworks).

Firstly, you need to be able to inject a DataAccessLayer<UtilClass> into ServiceLayer , eg

class ServiceLayer {
  private final DataAccessLayer<UtilClass> dal;

  ServiceLayer(DataAccessLayer<UtilClass> dal) {
    this.dal = dal;
  }

  public Integer testingMethode() {
    UtilClass info = dal.getData(UtilClass.class, 1);
    return info.getSomeFieldWithIntegerValue();
  }
}

This breaks the static coupling to the DataAccessLayer<UtilClass> created by the use of the new .

Now, you can create a mocked instance of DataAccessLayer<UtilClass> by creating a non-generic subclass:

class UtilClassDataAccessLayer extends DataAccessLayer<UtilClass> {}

and then create a mock instance:

DataAccessLayer<UtilClass> mocked = mock(UtilClassDataAccessLayer.class);

Now, you can configure this mock as you need, and pass it into the ServiceLayer :

ServiceLayer serviceLayer = new ServiceLayer(mocked);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM