【Java单元测试】实习项目实际问题6--被测试类中有私有方法的处理

问题简单描述

FordAmbientLightManager类中存在私有方法AmbientLightCanMsg,对私有方法的处理

具体情况

  1. 被测试类Java代码
public class FordAmbientLightManager extends FordCommonManager {

    private static final String TAG = "FordAmbientLightManager";
    private static FordAmbientLightManager manager;
    private AmbientLightSettingRsp ambientLightSettingRsp;
    private List<IAmbientLightChange> mIAmbientLightChangeListeners;

    public static synchronized FordAmbientLightManager getInstance(Context context) {
        if (null == manager) {
            manager = new FordAmbientLightManager(context);
        }
        return manager;
    }

    public FordAmbientLightManager(Context context) {
        super(context, APPID_ID_1021);
        mIAmbientLightChangeListeners = new ArrayList<>();
        addIResponseCallbackListener(mCallback);
        addISynchronizationInterfaceListener(mISynchronizationInterface);
    }

    private IResponseCallback mCallback = this::AmbientLightCanMsg;

    private void AmbientLightCanMsg(int commandid, BaseResponse baseResponse) {
        if (baseResponse == null) {
            Log.d(TAG, "AmbientLightCanMsg:baseResponse is null");
            return;
        }
        switch (commandid) {
            case COMMANDID_ID_737A:
                ambientLightSettingRsp = new AmbientLightSettingRsp(baseResponse);
                Log.d(TAG, "AmbientLightCanMsg:ambientLightSettingRsp= " + ambientLightSettingRsp.toString());
                ...//省略了很多代码
                
                for (IAmbientLightChange listener : mIAmbientLightChangeListeners) {
                    if (listener != null) {
                        listener.onAmbientLightChange(TYPE_AMBL_ALM_SET, ambL_alm_set);
                        ...//省略了很多代码
                    }

                }
                break;
            default:
                break;
        }
    }

  1. 问题具体分析
    首先是私有类如何处理的问题,具体解决可以看测试代码中的注释(后面没有说的默认在测试代码里)
    其次是for循环如何处理?梳理逻辑后其实是遍历了一个名为mIAmbientLightChangeListeners的监听器列表,并对列表中的每一个监听器执行一个特定的方法调用,以通知它们环境光变化的事件。理清楚在干嘛就好测试了。
  2. 具体测试代码
class FordAmbientLightManagerUT extends BaseUnitTest {


    private FordAmbientLightManager fordAmbientLightManager;
    private Context context;
    private AutoCloseable closeable;

    @BeforeAll
    public static void beforeAll() {
        mockStatic(Log.class);
        mockStatic(Car.class);
    }

    @BeforeEach
    public void beforeEach() {
        closeable = MockitoAnnotations.openMocks(this);
        Context context = Mockito.mock(Context.class);
        Mockito.when(context.getApplicationContext()).thenReturn(context);
        fordAmbientLightManager = FordAmbientLightManager.getInstance(context);
    }

    @AfterEach
    public void afterEach() throws IllegalAccessException, NoSuchFieldException {
        try {
            closeable.close();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        Class c = fordAmbientLightManager.getClass();
        Field manager = c.getDeclaredField("manager");
        manager.setAccessible(true);
        manager.set(fordAmbientLightManager, null);

    }


    @Test
    public void testAmbientLightCanMsg_COMMMANDID_737A() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {
        //PREPARE

        Class c = fordAmbientLightManager.getClass();
        //构造我们想要的baseResponse作为参数传进函数,其实也可以直接使用mock(mock后得到的baseResponse中的变量默认为0),根据后续代码baseResponse的具体值是多少并不影响函数
        BaseResponse test_baseResponse = new BaseResponse();
        byte[] response = new byte[20];
        Arrays.fill(response, (byte) 1);
        test_baseResponse.response = response;
        test_baseResponse.response_len = 20;

        //构造需要的list并通过反射调用设置私有变量的值
        List<FordAmbientLightManager.IAmbientLightChange> test_IAmbientLightChange = new ArrayList<>();
        FordAmbientLightManager.IAmbientLightChange IAmbientLightChange = Mockito.mock(FordAmbientLightManager.IAmbientLightChange.class);
        test_IAmbientLightChange.add(IAmbientLightChange);
        Field mIAmbientLightChangeListeners = getField(fordAmbientLightManager, "mIAmbientLightChangeListeners");
        setField(mIAmbientLightChangeListeners, fordAmbientLightManager, test_IAmbientLightChange);


        //使用反射,method关键字调用私有方法
        Method method_ambientLightCanMsg = c.getDeclaredMethod("AmbientLightCanMsg", int.class, BaseResponse.class);
        method_ambientLightCanMsg.setAccessible(true);
        //INVOKE
        //调用的具体格式
        method_ambientLightCanMsg.invoke(fordAmbientLightManager, COMMANDID_ID_737A, test_baseResponse);

        //VERIFY
        //验证被调用:逻辑就是,我们提供的监听器列表中有一个监听器,那么就会执行语句一次,验证的时候通过就代表测试通过
        verify(IAmbientLightChange).onAmbientLightChange(0, 1);
    }
}

存在问题

  1. 使用反射
  2. 选择了构造而不是直接使用mock,在构造时需要考虑对象结构,而mock时不需要,可以减少工作量

技术细节

1.method
2.验证逻辑

小结

  1. 在私有函数怎么测试的部分卡了不少时间,其实很简单使用method就可以搞定
  2. 因为代码水平很差,Java上过课但是基本上是全部忘记了,在看for (IAmbientLightChange listener : mIAmbientLightChangeListeners) 的时候想了一会,不理解是干嘛的(😢好菜啊我),后来问的gpt
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值