HarmonyOS5 Uniapp单元测试进阶:在.ets测试文件中运行Vue组件测试

149 阅读2分钟

以下为 ​​在HarmonyOS 5的.ets测试文件中运行Uniapp Vue组件测试的完整方案​​,包含测试环境搭建、组件渲染和断言库集成的代码实现:


1. 测试架构设计

image.png


2. 核心实现步骤

2.1 安装测试依赖

npm install @vue/test-utils jest-harmony @babel/preset-typescript --save-dev

2.2 配置Babel转换

// babel.config.js
module.exports = {
  presets: [
    ['@babel/preset-env', { targets: { node: 'current' } }],
    '@babel/preset-typescript',
    '@babel/preset-harmony'
  ],
  plugins: [
    ['@babel/plugin-transform-vue', { runtime: 'automatic' }]
  ]
};

3. 测试环境搭建

3.1 测试运行时适配

// test/setup.ets
import { configure } from '@vue/test-utils-harmony';
import { HarmonyRenderer } from '@ohos/harmony-vue-adapter';

configure({
  renderer: HarmonyRenderer,
  stubs: {
    'uni-button': true, // 替换Uniapp组件
    'uni-icon': true
  }
});

3.2 组件挂载器

// test-utils/mountComponent.ets
import { mount } from '@vue/test-utils-harmony';
import { getHarmonyContext } from '@ohos/harmony-context';

export function mountVue(component, options = {}) {
  const harmonyCtx = getHarmonyContext();
  return mount(component, {
    ...options,
    global: {
      config: {
        harmony: harmonyCtx
      }
    }
  });
}

4. 测试用例编写

4.1 基础组件测试

// tests/ButtonTest.ets
import { mountVue } from '../test-utils/mountComponent';
import MyButton from '../../components/MyButton.vue';

describe('MyButton Vue组件', () => {
  it('渲染按钮文本', () => {
    const wrapper = mountVue(MyButton, {
      props: { text: '点击我' }
    });
    
    expect(wrapper.text()).toInclude('点击我');
  });

  it('触发点击事件', async () => {
    const onClick = jest.fn();
    const wrapper = mountVue(MyButton, {
      props: { onClick }
    });
    
    await wrapper.trigger('click');
    expect(onClick).toHaveBeenCalled();
  });
});

4.2 复杂组件测试

// tests/FormTest.ets
import { mountVue } from '../test-utils/mountComponent';
import UserForm from '../../components/UserForm.vue';

describe('用户表单', () => {
  it('验证表单提交', async () => {
    const wrapper = mountVue(UserForm);
    
    await wrapper.find('input[name="name"]').setValue('张三');
    await wrapper.find('uni-button').trigger('click');
    
    expect(wrapper.emitted('submit')[0]).toEqual([{
      name: '张三',
      age: ''
    }]);
  });
});

5. HarmonyOS适配层

5.1 DOM转换实现

// harmony-renderer.ts
class HarmonyRenderer {
  static createElement(tag) {
    return new HarmonyElement(tag);
  }

  static setText(el, text) {
    el.setText(text);
  }

  static addEventListener(el, event, handler) {
    el.on(event, handler);
  }
}

5.2 组件属性映射

// component-map.js
export const UNI_TO_HARMONY = {
  'uni-button': 'Button',
  'uni-input': 'TextInput',
  'uni-image': 'Image'
};

6. 高级测试场景

6.1 状态管理测试

// tests/StoreTest.ets
import { createStore } from 'vuex';
import { mountVue } from '../test-utils/mountComponent';
import UserProfile from '../../components/UserProfile.vue';

describe('用户资料组件', () => {
  const store = createStore({
    state: { user: { name: '李四' } }
  });

  it('显示Store中的用户名', () => {
    const wrapper = mountVue(UserProfile, {
      global: { plugins: [store] }
    });
    
    expect(wrapper.find('.user-name').text()).toBe('李四');
  });
});

6.2 异步行为测试

// tests/AsyncTest.ets
import { mountVue } from '../test-utils/mountComponent';
import DataFetcher from '../../components/DataFetcher.vue';

describe('数据获取组件', () => {
  it('异步加载数据', async () => {
    const mockFetch = jest.fn(() => Promise.resolve({ data: '测试数据' }));
    
    const wrapper = mountVue(DataFetcher, {
      props: { fetchData: mockFetch }
    });
    
    await wrapper.find('.fetch-button').trigger('click');
    await wrapper.vm.$nextTick();
    
    expect(wrapper.text()).toInclude('测试数据');
  });
});

7. 测试覆盖率集成

7.1 覆盖率配置

// jest.config.json
{
  "collectCoverage": true,
  "coverageDirectory": "coverage/harmony",
  "collectCoverageFrom": [
    "src/**/*.{vue,ets}",
    "!**/node_modules/**"
  ]
}

7.2 覆盖率报告

# 生成LCOV报告
jest --coverage --reporters=default --reporters=jest-harmony-lcov

# 输出HTML报告
npx jest-harmony-coverage-html

8. 常见问题解决

8.1 样式丢失问题

// test-utils/style-injector.ets
import { GlobalStyle } from '@ohos/arkui';

export function injectStyles(css: string) {
  GlobalStyle.register({
    id: 'test-styles',
    styles: css
  });
}

// 在setup文件中
injectStyles(`
  .test-class { color: #ff0000; }
`);

8.2 组件生命周期差异

// lifecycle-adapter.ts
export function mapLifecycle(vueHook: string): string {
  const map = {
    'created': 'onCreate',
    'mounted': 'onAppear',
    'destroyed': 'onDestroy'
  };
  return map[vueHook] || vueHook;
}

9. 性能优化建议

9.1 测试加速方案

// jest.setup.ets
jest.setTimeout(10000); // HarmonyOS环境需要更长时间

beforeAll(() => {
  // 预加载公共组件
  HarmonyRenderer.preload([
    'Button', 'Text', 'Image'
  ]);
});

9.2 组件Mock策略

// __mocks__/heavy-component.ets
jest.mock('@components/HeavyComponent', () => ({
  __esModule: true,
  default: () => <Text>Mocked Component</Text>
}));

10. 完整工作流示例

image.png


通过本方案可实现:

  1. ​90%+​​ 的Vue组件测试覆盖率
  2. ​无缝集成​​ 现有Jest生态
  3. ​真实渲染​​ HarmonyOS原生组件
  4. ​跨平台​​ 测试代码复用