前端自动化测试-BDD-集成测试

Posted lin-fighting

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端自动化测试-BDD-集成测试相关的知识,希望对你有一定的参考价值。

何为BDD(Behavior Driven Development)

  • 行为驱动的开发,是一种敏捷软件开发的技术,他鼓励软件项目中的开发者,QA和非技术人员或商业参与者之间的协作。

  • 以上节TDD的代码为例子,将测试文件全部删除。从BDD的角度出发,开发人员不需要先写测试用例,直接编写代码即可。

  • 编写完代码之后,第二步就是需要站在用户的角度,怎么去测试,测试的重点是什么?从用户的行为角度出发,编写我们的测试用例。因为用户行为是连续的,比如toDOList案例,用户希望在输入框输入内容按下回车后,list组件可以显示出来。他们需要不同组建的一个协同。

  • 所以BDD一般是结合集成测试来开发。

  • 还是以todoList案例,等我们编写玩开发代码,站在用户的角度出发,我希望,输入内容之后,按下回车,能在List里面看到我想要的内容。根据这个需求编写测试用例。


it(`
 1. 用户输入输入框内容
 2. 点击回车
 3. 列表中显示输入的内容
`, () => 
  // 多个组件,使用mount
  const wrapper = mount(<TodoList />);
  const inputElm = wrapper.find("[data-test='input']");

  const inputValue = "用户输入了内容";
  // 模拟用户第一个行为
  inputElm.simulate("change", 
    target:  value: inputValue ,
  );
  // 点击回车
  inputElm.simulate("keyUp", 
    keyCode: 13,
  );
  // 列表中应该显示内容
  const listItems = wrapper.find("[data-test='list-item']");
  console.log('listItems', listItems.debug());
  expect(listItems.length).toBe(1);
  expect(listItems.text()).toBe(inputValue);
);

如上,因为涉及到多个组件,所以必须使用mount渲染,然后模拟用户输入内容,触发回车事件,然后判断列表中是不是真的多出一个list。

测试通过。

何为BDD?

通过简单的案例我们可以知道BDD,是以用户行为驱动的开发,

  • 流程就是 先根据功能编写代码,等功能开发完毕之后,
  • 站在用户的角度出发,去编写测试用例。
  • Bdd一般结合集成测试。
  • 而TDD则是以测试驱动的开发,先编写测试用例,再开发代码。
  • TDD一般结合单元测试。主要是为每个组件的功能进行测试。

TDD & BDD

TDD:

  • 先写测试再写代码
  • 一般结合单元测试,是白盒测试
  • 测试重点在代码
  • 安全感第
  • 速度快(shallow)
    BDD:
  • 先写代码,再写测试
  • 一般结合集成测试使用,是黑盒测试
  • 测试重点在UI(DOM)
  • 安全感高
  • 速度慢(mount)

如何进行Redux的测试

如果使用TDD进行redux的测试,那么action ,reducer等等都需要编写测试用例,代码量大。也不能保证数据流正确。
所以得使用BDD来进行redux的测试比较好。

  • 还是以toDOlist案例,把状态存储到redux去。
    改造一下demo:
    Header组件
function Headers( onAddItem : Props) 
  const dispatch = useDispatch();
  const [value, setValue] = React.useState("");

  return (
    <div className="Header">
      <input
        className="header-input"
        type="text"
        data-test="input"
        value=value
        onChange=(e) => 
          setValue(e.target.value);
        
        onKeyUp=(e) => 
          if (e.keyCode === 13) 
            dispatch( type: "dispatch", value );
            setValue("");
           else 
            return;
          
        
      />
    </div>
  );

list组件


function TodoList() 
  const  items  = useSelector(
    (state: any) => ( items: state.todo.items ),
    shallowEqual
  );
  return (
    <div>
      <Headers />
      <div>------------</div>
      items.map((ctem) => 
        return (
          <div data-test="list-item" key=ctem>
            ctem
          </div>
        );
      )
    </div>
  );

将状态存储到redux去。
然后重新编写刚才的测试用例


it(`
 1. 用户输入输入框内容
 2. 点击回车
 3. 列表中显示输入的内容
`, () => 
  // 多个组件,使用mount
  const wrapper = mount(
    <Provider store=store>
      <TodoList />
    </Provider>
  );
  const inputElm = wrapper.find("[data-test='input']");

  const inputValue = "用户输入了内容";
  // 模拟用户第一个行为
  inputElm.simulate("change", 
    target:  value: inputValue ,
  );
  // 点击回车
  inputElm.simulate("keyUp", 
    keyCode: 13,
  );
  // 列表中应该显示内容
  const listItems = wrapper.find("[data-test='list-item']");
  console.log("listItems", listItems.debug());
  expect(listItems.length).toBe(1);
  expect(listItems.text()).toBe(inputValue);
);

这里需要跟外部一样,将store传进去给他。这里只是需要更改一下moutn的内容,其他东西根本不需要更改,这也是BDD的好处。

  • 开发业务代码不用考虑测试用例
  • 业务代码如果更改比如从state到redux,那么BDD测试用例是很少改动的,因为他不是基于代码的,而是基于用户角度,只要流程跑的通,就正确。(耦合度低)
  • BDD驱动的集成测试,安全感较高,因为这个测试用例一通过,就表示这个功能是可以用的。而不会出现单元测试无法保证多个组件结合在一起的可用性。
  • BDD的代码覆盖率会降低,但是影响不大。

异步代码的测试

  • 还是以todoList为例
  • 实现一个功能,在页面初始化的时候i请求数据,展示List。
 useEffect(() => 
 // 测试的时候我们不需要发送请求
    axios.get("/xxx").then((res: any) => 
      setItem((pre) => [...pre, ...res.data]);
    );
  , []);

功能写完编写测试用例

因为测试用例需要执行组件,所以模拟axios
__mocks__/axios.ts
export default 
  get(url) 
    if (url === "/xxx") 
      return new Promise((r) => 
          r( data: ["test1", "test2"] );
      );
    
  ,
;

当我们使用mount渲染组件的时候,里面遇到axios的地方就会去上面获取,如上,返回了test1 test2。

test(`1.用户打开页面
    2. 展示接口返回的数据
`, (done) => 
  const wrapper = mount(
    <Provider store=store>
      <TodoList />
    </Provider>
  );
  // 异步需要等待
  setTimeout(() => 
    wrapper.update()
    console.log(wrapper.debug())
    const listItems = (wrapper as any).find("[data-test='list-item']");
    expect(listItems.length).toBe(2);
    done()
  , 1000);
);

因为请求时异步的,所以我们需要等待一下才能判断,然后wrapper.update去更新dom,此时dom已经有了请求回来的数据。因为这里是异步,所以需要用到done。
看下更新后的dom:
第一次渲染

 console.log
      <Provider store=...>
        <TodoList>
          <div>
            <Headers>
              <div className="Header">
                <input className="header-input" type="text" data-test="input" value="" onChange=[Function: onChange] onKeyUp=[Function: onKeyUp] />
              </div>
            </Headers>
            <div>
              ------------
            </div>
          </div>
        </TodoList>
      </Provider>

没有List。
更新后:

<Provider store=...>
        <TodoList>
          <div>
            <Headers>
              <div className="Header">
                <input className="header-input" type="text" data-test="input" value="" onChange=[Function: onChange] onKeyUp=[Function: onKeyUp] />
              </div>
            </Headers>
            <div>
              ------------
            </div>
            <div data-test="list-item">
              test1
            </div>
            <div data-test="list-item">
              test2
            </div>
          </div>
        </TodoList>
      </Provider>

已经渲染出list了。

  • setTimeout的异步怎么优化?不可能等待所有setTImeout完成的。
  useEffect(() => 
    setTimeout(() => 
      axios
        .get("/xxx")
        .then((res: any) => 
          console.log("res----------", res);
          setItem((pre) => [...pre, ...res.data]);
        )
        .catch((err) => 
        );
    , 5000);
  , []);

需要使用jests提供的setTImoeut

eforeEach(() => 
    console.log(123123);
    
  jest.useFakeTimers();
);
afterEach(() => 
  jest.useRealTimers();
);
test(`1.用户打开页面
    2. 展示接口返回的数据
`, (done) => 
    // 将所有setTimoeut里面执行
  const wrapper = mount(
    <Provider store=store>
      <TodoList />
    </Provider>
  );
  jest.runAllTimers();
  // 异步需要等待
  setTimeout(() => 
    wrapper.update();
    const listItems = (wrapper as any).find("[data-test='list-item']");
    expect(listItems.length).toBe(2);
    done();
  , 1000);
);
Test Suites: 2 passed, 2 total
Tests:       2 passed, 2 total
Snapshots:   1 obsolete, 0 total
Time:        6.854 s

这样就可以了。

总结:

  • BDD就是先开发组件的功能,然后从用户角度出发,编写一套流程的测试用例,而不是单个单个的,当测试用例通过的时候,就表示这些组件结合起来的功能是完整的。
  • 而TDD是先编写测试用例,(单元测试),然后再开发代码,可以保证每个组件的功能完整性,但是不能保证这些组件结合起来后的功能是否完整。
  • BDD+集成测试对于业务代码是一个相对更好的方案,TDD+单元测试对于单独的函数库来说会更好。

前端自动化测试的优势

  • 更好的代码组织,项目的可维护性增强
  • 更小的bug出现概率,尤其回归测试的bug
  • 修改工程质量差的项目,更加安全

以上是关于前端自动化测试-BDD-集成测试的主要内容,如果未能解决你的问题,请参考以下文章

vue-cli 项目集成 Jest 单元测试

前端测试 karma mocha should 都是什么鬼?

前端自动化测试:TDD 和 BDD 哪个更好一些?

如何提高UI自动化测试的质量

TiD精彩回顾| 京东商城代码质量平台建设实践

饮食、睡眠和呼吸单元测试/TDD/BDD [关闭]