转载自: https://semaphoreci.com/community/tutorials/how-to-test-react-and-mobx-with-jest?utm_content=buffer15b42&utm_medium=social&utm_source=twitter.com&utm_campaign=buffer

Introduction

If you’re developing React applications, then you know that the React community has been bursting with new ideas and tools over the last year. When investigating any new technology to incorporate into a stack, we should consider if it either makes the workflow much easier, or solves a key problem. MobX and Enzyme are 2 new libraries in the React world that fit the bill. This sample todo application is incredibly easy to build with React and MobX, and in this article we’ll cover unit and UI/functional testing for React and MobX with Enzyme.

Code Smell in React

There’s no shortage of ways to build applications with React, but one thing is for sure — React shines the brightest when it is used as a reactive view layer sitting on top of the state of your application. If you use it for more than that, e.g. the UI is responsible for determining when and how to load data or the UI stores certain aspects of state, this can quickly lead to code smell.

In order to keep our React projects from growing messy, we need store the application state completely outside of the UI. This will not only make our application more stable, but it will also make testing extremely simple, as we can test the UI and the state separately.

Enter MobX

While frameworks and patterns like Flux, Redux and Relay give you a powerful way to manage the state of your application outside of the React view layer, they are complicated to set up, and it can take a while to make simple changes. One of the big reasons that MobX is quickly growing in popularity is the fact that it is very simple, which is nearly as simple as coding an object literal . That means you get a tremendous developer experience without sacrificing application stability.

What is Enzyme?

Enzyme will be our testing tool of choice, since it will allow us to test React components with jQuery-like syntax. This means functional and integration-style tests will be easy to write.

Accessing the Code:

  • You can find the code for the simple todo application on GitHub: learncodeacademy/react-js-tutorials,

    • If you’re new to MobX, you can see the video tutorial for this application here,
    • If you don’t have git, you can install it here, and
    • There are 2 branches: master for where we begin and mobx-testing for where we’ll end up after finishing this article.
  • The only requirement is Node.js version 4 or higher,
  • To get it up and running, type npm install && npm start, and
  • Visit the application on localhost:8080.

Now, let’s get to testing this application.

Installing Enzyme and Jest

While Mocha works great with Enzyme, Jest is a little bit simpler to set up. Our 3 testing dependencies will be: jest for testing, babel-jest for transpiling our ES6, and enzyme for our functional React tests. Let’s clone the repository, then run npm install and also install those dependencies.

  1. git clone git@github.com:learncodeacademy/react-js-tutorials.git
  2. cd react-js-tutorials/6-mobx-react
  3. npm install
  4. npm install --save-dev enzyme jest babel-jest

With these packages installed, Jest is fully configured out of the box. Babel will still work great as long as Babel is configured with a .babelrc file. Some people configure Babel in webpack.config.js, if that’s you, then you’ll need to move it to the .babelrc file so Jest and Webpack can both use the config.

We can now run jest and notice that no tests are found:

  1. $ jest
  2. Using Jest CLI v14.1.0, jasmine2, babel-jest
  3. NO TESTS FOUND. 5 files checked.
  4. testPathDirs: /Users/cmn/Code/sandbox/react-mobx - 5 matches
  5. testRegex: __tests__/.*.js$ - 0 matches
  6. testPathIgnorePatterns: /node_modules/ - 5 matches

  

Unit Testing with MobX

Since MobX classes behave like object literals, testing is incredibly simple. Let’s begin by unit testing our TodoList store. Jest will run anything in the __tests__ directory by default, so let’s run these 2 commands to make the directory as well as our first test file.

  1. mkdir __tests__
  2. touch __tests__/TodoStore.test.js

Jasmine is the default runner for Jest, so we have access to describeit, and expect without configuring anything. This means that we can get straight to writing our first test.

  1. import { TodoStore } from "../src/js/TodoStore"
  2.  
  3. describe("TodoStore", () => {
  4. it("creates new todos", () => {
  5. const store = new TodoStore
  6. store.createTodo("todo1")
  7. store.createTodo("todo2")
  8. expect(store.todos.length).toBe(2)
  9. expect(store.todos[0].value).toBe("todo1")
  10. expect(store.todos[1].value).toBe("todo2")
  11. })

  We created a new TodoStore, did some actions and observed the result just as if it were an object literal. The biggest advantage of MobX is its simplicity. Any changes we made would have been passed onto any observers as well. It’s important to note that we imported the store constructor { TodoStore } and not the default export, which is an instantiated store. This allows our next test to instantiate its own fresh store as well:

  1. it("clears checked todos", () => {
  2. const store = new TodoStore
  3. store.createTodo("todo1")
  4. store.createTodo("todo2")
  5. store.createTodo("todo3")
  6. store.todos[1].complete = true;
  7. store.todos[2].complete = true;
  8. store.clearComplete()
  9.  
  10. expect(store.todos.length).toBe(1)
  11. expect(store.todos[0].value).toBe("todo1")
  12. })

  

With unit testing in place, let’s use Enzyme to do some unit tests against our UI layer:

Unit Testing with React and Enzyme

Let’s being by making the file:

  1. touch __tests__/TodoList.unit.test.js

  

Again, since MobX stores act just like object literals, we can test our React component by injecting it with any object literal to simulate a store state. We can use a single beforeEach to provide this state to all tests:

  1. import { shallow } from 'enzyme'
  2. import React from "react"
  3.  
  4. import TodoList from "../src/js/TodoList"
  5.  
  6. describe("TodoList", function() {
  7. //don't use an arrow function...preserve the value of "this"
  8. beforeEach(function() {
  9. this.store = {
  10. filteredTodos: [
  11. {value: "todo1", id: 111, complete: false},
  12. {value: "todo2", id: 222, complete: false},
  13. {value: "todo3", id: 333, complete: false},
  14. ],
  15. filter: "test",
  16. createTodo: (val) => {
  17. this.createTodoCalled = true
  18. this.todoValue = val
  19. },
  20. }
  21. })
  22.  
  23. //tests will go here and receive this.store
  24.  
  25. })

  Notice how we do not use an ES6 arrow function for the beforeEach? We want to make sure that the value of this remains the same or this.store will not get passed on to our tests. When using context for tests, it’s a good idea to stop using arrow functions. However, we want to use an arrow function on our createTodo function, so we can set this.todoClicked and this.todoValue on the parent context when it gets called.

Now, adding a test is straightforward:

  1. //don't use an arrow function, preserve the value of "this"
  2. it("renders filtered todos", function() {
  3. const wrapper = shallow(<TodoList store={this.store} />)
  4.  
  5. expect(wrapper.find("li span").at(0).text()).toBe("todo1")
  6. expect(wrapper.find("li span").at(1).text()).toBe("todo2")
  7. expect(wrapper.find("li span").at(2).text()).toBe("todo3")
  8. })

  

We use Enzyme to create a wrapper for our store-injected-component, then we can ensure that all 3 todos printed correctly. Now, let’s add some tests that simulate user interaction on the component:

  1. it("calls createTodo on enter", function() {
  2. const wrapper = shallow(<TodoList store={this.store} />)
  3.  
  4. wrapper.find("input.new").at(0)
  5. .simulate("keypress", {which: 13, target: {value: 'newTodo'}})
  6.  
  7. expect(this.createTodoCalled).toBe(true)
  8. expect(this.todoValue).toBe("newTodo")
  9. })
  10.  
  11. it("updates store filter", function() {
  12. const wrapper = shallow(<TodoList store={this.store} />)
  13.  
  14. wrapper.find("input.filter").at(0)
  15. .simulate('change', {target: {value: 'filter'}})
  16.  
  17. expect(this.store.filter).toBe("filter")
  18. })

  

Enzyme allows us to easily simulate real JS events. The first argument of simulate is the event type, and the 2nd argument is the event object. Now, we have verified that the component calls createTodo when todos are created and also updates the filter when changed.

Integration Tests

Every now and then, you may find it useful to test that components work together the way they should. If you want to do this with React and MobX, you should simply replace the mock store with a real MobX store. Create TodoList.functional.test.js and add this:

  1. import { shallow } from 'enzyme'
  2. import React from "react"
  3.  
  4. import TodoList from "../src/js/TodoList"
  5. import { TodoStore } from "../src/js/TodoStore"
  6.  
  7. describe("TodoList.functional", () => {
  8.  
  9. it("filters todos", () => {
  10. const store = new TodoStore
  11.  
  12. store.createTodo("todo1")
  13. store.createTodo("todo2")
  14. store.createTodo("todo3")
  15. store.filter = "2"
  16.  
  17. const wrapper = shallow(<TodoList store={store} />)
  18.  
  19. expect(wrapper.find("li").length).toBe(1)
  20. expect(wrapper.find("li span").at(0).text()).toBe("todo2")
  21. })
  22. })

We are able to verify that the component behaves correctly with an actual MobX store as well. We can also verify that user interaction modifies the store appropriately:  

  

  1. it("clears completed todos when 'clear completed' is clicked", () => {
  2. const store = new TodoStore
  3.  
  4. store.createTodo("todo1")
  5. store.createTodo("todo2")
  6. store.createTodo("todo3")
  7. store.todos[0].complete = true
  8. store.todos[1].complete = true
  9.  
  10. const wrapper = shallow(<TodoList store={store} />)
  11.  
  12. wrapper.find("a").simulate("click")
  13.  
  14. expect(wrapper.find("li").length).toBe(1)
  15. expect(wrapper.find("li span").at(0).text()).toBe("todo3")
  16. expect(store.todos.length).toBe(1)
  17. })

  

Notice our expect at the bottom, we can verify that both the UI and the store changed appropriately when the “clear completed” link is clicked.

[Web] How to Test React and MobX with Jest的更多相关文章

  1. [Web 前端] 如何构建React+Mobx+Superagent的完整框架

    ReactJS并不像angular一样是一个完整的前端框架,严格的说它只是一个UI框架,负责UI页面的展示,如果用通用的框架MVC来说,ReactJs只负责View了,而Angular则是一个完整的前 ...

  2. Facebook的Web开发三板斧:React.js、Relay和GraphQL

    2015-02-26 孙镜涛  InfoQ Eric Florenzano最近在自己的博客上发表了一篇题为<Facebook教我们如何构建网站>的文章,他认为软件开发有些时候需要比较大的跨 ...

  3. React使用Mobx管理数据

    React 和 Vue一样都属于单向数据流,为了更好的进行状态和数据管理官方和第三方也有配套的Redux等插件,本文介绍一个个人觉得更易用使用的组件 Mobx 核心概念 MobX 处理你的应用程序状态 ...

  4. react使用mobx

    mobx api 使用装饰器语法 mobx数据转化为js数据 安装 yarn add mobx mobx-react yarn add babel-preset-mobx --dev "pr ...

  5. react+react-router+mobx+element打造管理后台系统---react-amdin-element

    react-admin-element,一款基于react的后台管理系统. 那么我们和其他的后台管理系统有什么区别呢? demo地址:点我进入demo演示 github地址:点我进入github 1. ...

  6. 【Web技术】401- 在 React 中使用 Shadow DOM

    本文作者:houfeng 1. Shadow DOM 是什么 Shadow DOM 是什么?我们先来打开 Chrome 的 DevTool,并在 'Settings -> Preferences ...

  7. [Web 前端] 如何在React中做Ajax 请求?

    cp from : https://segmentfault.com/a/1190000007564792 如何在React中做Ajax 请求? 首先:React本身没有独有的获取数据的方式.实际上, ...

  8. 【前端单元测试入门05】react的单元测试之jest

    jest jest是facebook推出的一款测试框架,集成了前面所讲的Mocha和chai,jsdom,sinon等功能. 安装 npm install --save-dev jest npm in ...

  9. 从零配置webpack(react+less+typescript+mobx)

    本文目标 从零搭建出一套支持react+less+typescript+mobx的webpack配置 最简化webpack配置 首页要初始化yarn和安装webpack的依赖 yarn init -y ...

随机推荐

  1. jQuery事件(四)

    一.基本事件函数下面事件函数中参数相关说明:eventType:事件类型,字符串'click' 'submit'多个事件类型可以通过用空格隔开[一次性绑定'click submit']eventDat ...

  2. 70.JS---利用原生js做手机端网页自适应解决方案rem布局

    利用原生js做手机端网页自适应解决方案rem布局 刚开始我用的是下面这段代码,然后js通过外部链接引入,最后每次用手机刷新网页的时候都会出现缩略图 function getRem(pwidth, pr ...

  3. Springboot2 jpa druid多数据源

    package com.ruoyi; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans ...

  4. 为什么MES系统要定制化?看这三家汽车供应商的苦恼

    很多企业对于为什么要对MES系统进行选择和定制化很不理解,今天,小编通过一个故事给大家进行阐述—— 故事背景: 汽车电子行业的三家企业A,B,C. A是整车厂一级供应商,主要产品为汽车电子配电盒. B ...

  5. Tomcat get请求中文参数乱码

    场景:使用Tomcat容器进行get方式传递中文参数到后台乱码. 原因:Tomcat默认的编码方式是ISO--. 解决方案: . 设置cofg-server.xml中的<Connector> ...

  6. free - 显示系统内存信息

    free - Display amount of free and used memory in the system 显示系统中空闲和已使用内存的数量 格式: free [options] opti ...

  7. 提高python处理数据的效率方法

    处理大数据的方法有很多,目前我知道就这么多,后面会持续更新: 一.将数据分批次读取 csv格式是常见的数据存储方式,对于我们普通人而言易于读写.此外,在pandas中有pd.read_csv()函数可 ...

  8. Mac下Mysql配置

    安装 http://www.mirrorservice.org/sites/ftp.mysql.com/Downloads/MySQL-5.6/mysql-5.6.41-macos10.13-x86_ ...

  9. 性能测试基础---LR关联

    ·什么时候需要做关联?一般来说,在脚本运行出错的时候,我们就可能需要进行关联处理. ·脚本出错分为两种情况: ·直接回放出错(失败).通常来说,如果录制成功,回放失败,排除数据的唯一性约束之后,那就必 ...

  10. django项目中使用KindEditor富文本编辑器。

    先从官网下载插件,放在static文件下 前端引入 <script type="text/javascript" src="/static/back/kindedi ...