问题简单描述
FordAmbientLightManager类中存在私有方法AmbientLightCanMsg,对私有方法的处理
具体情况
- 被测试类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;
}
}
- 问题具体分析
首先是私有类如何处理的问题,具体解决可以看测试代码中的注释(后面没有说的默认在测试代码里)
其次是for循环如何处理?梳理逻辑后其实是遍历了一个名为mIAmbientLightChangeListeners的监听器列表,并对列表中的每一个监听器执行一个特定的方法调用,以通知它们环境光变化的事件。理清楚在干嘛就好测试了。 - 具体测试代码
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);
}
}
存在问题
- 使用反射
- 选择了构造而不是直接使用mock,在构造时需要考虑对象结构,而mock时不需要,可以减少工作量
技术细节
1.method
2.验证逻辑
小结
- 在私有函数怎么测试的部分卡了不少时间,其实很简单使用method就可以搞定
- 因为代码水平很差,Java上过课但是基本上是全部忘记了,在看for (IAmbientLightChange listener : mIAmbientLightChangeListeners) 的时候想了一会,不理解是干嘛的(😢好菜啊我),后来问的gpt