Vuex Store 在测试之间以某种方式保持状态
Vuex Store keeps state somehow between tests
我在测试项目的前端部分时遇到一个奇怪的问题。我在前端使用 Vue 组件。该项目是教师为助手设置约会的网站,因此助手可以为 class.
准备好一切
首先让我解释一下结构。
我有一个按日期列出所有约会的组件。每个日期都是一张单独的卡片,一个日期的所有约会都是该卡片上的行。每行是一个特定的时隙。通过单击卡片顶部的按钮或单击行号,可以将约会添加到列表中。
所以我创建了三个组件:AppointmentsList.Vue 从后端获取约会并构建卡片列表,AppointmentsCard.Vue,它接收日期和该日期的所有约会作为道具,最后是 AppointmentRow.Vue,它在 table 的一行中显示约会的详细信息。状态、约会、请求的日期和其他数据保存在 Vuex 存储中。
我使用 TDD 构建项目,使用 Jest 和 Vue-test-utils 编写测试。 Mocks 用于模拟后端的响应。用卡片和行显示约会的测试工作正常。但是在测试按钮时遇到了一个奇怪的问题。
在下面的代码中,我向您展示了我的测试,为简洁起见进行了编辑。首先导入所有内容,然后定义后端对各种端点的响应。只有 appointmentResponse 很重要。请注意,有两个约会是 returned。
函数 createStore 从模块中构建一个商店。我将所有状态、吸气剂和突变保存在模块中。在每个测试 运行 之前,我创建一个新商店并使用商店的突变使用响应数据初始化商店。每次测试后,jest mocks 被清除,vue-test-utils 包装器被销毁。
*AppointmentList.spec.js*
import {mount, createLocalVue} from '@vue/test-utils'
import flushPromises from 'flush-promises'
import AppointmentsList from '../../resources/js/components/AppointmentsList.vue'
import axios from 'axios'
import Vuex from 'vuex'
import appointmentsmodule from '../../resources/js/storemodules/appointmentsModule.js'
import classhoursmodule from '../../resources/js/storemodules/classhoursModule.js'
import classroomsmodule from '../../resources/js/storemodules/classroomsModule.js'
import experimentsmodule from '../../resources/js/storemodules/experimentsModule.js'
import locationsmodule from '../../resources/js/storemodules/locationsModule.js'
import subjectsmodule from '../../resources/js/storemodules/subjectsModule.js'
import usersmodule from '../../resources/js/storemodules/usersModule.js'
import Vue from 'vue'
import { wrap } from 'lodash'
let appointmentResponse={"status":200,"lines":2,"data":[{"id":1,"subject_id":1,"owner_id":1,"group_id":1,"appointment_at":"2021-12-29T00:00:00.000000Z","classhour_id":1,"classroom_id":1,"experiment_id":null,"short_name":"magnam","description":"Sit cum quae quae quo quo consequatur.","demo":"0","toa_preferred_id":null,"location_id":1,"created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"},{"id":2,"subject_id":2,"owner_id":1,"group_id":1,"appointment_at":"2021-12-28T00:00:00.000000Z","classhour_id":1,"classroom_id":1,"experiment_id":null,"short_name":"possimus","description":"Velit eos sed esse reprehenderit.","demo":"0","toa_preferred_id":null,"location_id":1,"created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"}]}
let userResponse={"status":200,"lines":3,"data":[{"id":1,"code":"Est","name":"Mr. Americo Mertz I","email":"user1@hetstreek.nl","actual_location":"1","registrar":"1","email_verified_at":"2022-01-04T15:50:26.000000Z","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z","department_location_id":null,"welcome_valid_until":null,"roles":[{"id":2,"name":"toa","guard_name":"web","created_at":"2022-01-04T15:50:24.000000Z","updated_at":"2022-01-04T15:50:24.000000Z","pivot":{"model_id":"1","role_id":"2","model_type":"App\Models\User"}},{"id":1,"name":"beheerder","guard_name":"web","created_at":"2022-01-04T15:50:22.000000Z","updated_at":"2022-01-04T15:50:22.000000Z","pivot":{"model_id":"1","role_id":"1","model_type":"App\Models\User"}}]},{"id":2,"code":"Est","name":"Mr. Americo Mertz I","email":"user2@hetstreek.nl","actual_location":"2","registrar":"1","email_verified_at":"2022-01-04T15:50:26.000000Z","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z","department_location_id":null,"welcome_valid_until":null,"roles":[{"id":3,"name":"docent","guard_name":"web","created_at":"2022-01-04T15:50:25.000000Z","updated_at":"2022-01-04T15:50:25.000000Z","pivot":{"model_id":"2","role_id":"3","model_type":"App\Models\User"}}]},{"id":3,"code":"Est","name":"Mr. Americo Mertz I","email":"user3@hetstreek.nl","actual_location":"1","registrar":"1","email_verified_at":"2022-01-04T15:50:26.000000Z","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z","department_location_id":"{\"department_id\":1,\"location_id\":1,\"updated_at\":\"2022-01-04T15:50:26.000000Z\",\"created_at\":\"2022-01-04T15:50:26.000000Z\",\"id\":1}","welcome_valid_until":null,"roles":[{"id":4,"name":"leerling","guard_name":"web","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z","pivot":{"model_id":"3","role_id":"4","model_type":"App\Models\User"}}]}]}
let classhourResponse={"status":200,"lines":2,"data":[{"id":1,"name":"9","starttime":"15:22","endtime":"12:56","location_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"},{"id":3,"name":"1","starttime":"21:47","endtime":"20:16","location_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"}]}
let classroomResponse={"status":200,"lines":2,"data":[{"id":1,"name":"non","number":"756","in_use":"1","student_accessible":"0","teacher_accessible":"1","location_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"},{"id":3,"name":"ut","number":"214","in_use":"1","student_accessible":"0","teacher_accessible":"1","location_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"}]}
let experimentResponse={"status":200,"lines":2,"data":[{"id":1,"name":"nam","description":"Aliquam nihil voluptas aut vel neque.","student_selectable":"0","user_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"},{"id":3,"name":"similique","description":"Exercitationem officiis excepturi aut veniam voluptatum.","student_selectable":"1","user_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"}]}
let locationResponse={"status":200,"lines":2,"data":[{"id":1,"name":"Omnis.","school_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"},{"id":2,"name":"Illo.","school_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"}]}
let schoolResponse={"status":200,"lines":1,"data":[{"id":1,"schoolname":"Jaydon Mante","domain":"hetstreek.nl","max_locations":"1","payed_at":null,"due_date":null,"active":"1","storage_folder":"jaydonmante_hetstreeknl","created_at":"2022-01-05T14:41:49.000000Z","updated_at":"2022-01-05T14:41:49.000000Z"}]}
let subjectResponse={"status":200,"lines":2,"data":[{"id":1,"code":"SK","description":"Quis.","block_for_days":"9","color":"A3A3A3","created_at":"2022-01-05T18:54:41.000000Z","updated_at":"2022-01-05T18:54:41.000000Z"},{"id":2,"code":"re","description":"Libero.","block_for_days":"3","color":"A3A3A3","created_at":"2022-01-05T18:54:41.000000Z","updated_at":"2022-01-05T18:54:41.000000Z"}]}
function createStore(){
return {
modules:{
appointments:appointmentsmodule,
classhours:classhoursmodule,
classrooms:classroomsmodule,
experiments:experimentsmodule,
locations:locationsmodule,
subjects: subjectsmodule,
users: usersmodule
}
}
}
let wrapper
let store
beforeEach(()=>{
const newStore=createStore()
//define a new store for each test
store=new Vuex.Store(newStore)
//sets the range of dates for which the appointments need to be shown.
let startdate = new Date(2021, 11, 27)
let enddate = new Date(startdate)
enddate.setDate(startdate.getDate()+3)
//initialize the store
store.commit('setAppointmentsPeriod',{startdate,enddate})
store.commit('setSubjectFilter', [])
store.commit('storeAppointments',[])
store.commit('storeClasshours',classhourResponse.data)
store.commit('storeClassrooms',classroomResponse.data)
store.commit('storeExperiments',experimentResponse.data)
store.commit('storeLocations',locationResponse.data)
store.commit('storeSubjects',subjectResponse.data)
store.commit('storeUsers',userResponse.data)
//log to show the appointments are empty
console.log(store.state.appointments.appointments)
jest.clearAllMocks()
})
afterEach(()=>{
jest.clearAllMocks
wrapper.destroy()
})
jest.mock("axios")
const localVue =createLocalVue()
localVue.use(Vuex)
当组件 AppointmentsList 在测试中安装时,它会检测存储中的时间段变化,这会触发对后端的请求并加载该时间段的约会。
第一个测试测试卡片顶部的按钮。它触发按钮并验证添加约会的模式已打开,填写表单,触发提交按钮并验证数据是否已发送到后端,并且商店已更新,所以现在商店中应该有三个约会。这很好用。
test('add button adds appointment to list', async ()=>{
//set up the mock data.
let classhour=1
let appointmentToAdd={
id:3,
owner_id:1,
experiment_id:null,
short_name:"Schaduwpracticum",
description:"Lichtkastje met voeding, kartonnetje, scherm, liniaal",
group_id:1,
subject_id:1,
toa_preferred_id:1,
appointment_at:"2021-12-27",
classhour_id:classhour,
classroom_id:1,
demo:false,
location_id:1,
created_at:new Date(),
updated_at:new Date()
}
//mock axios responses. Get returns the appointments, post returns the added appointment.
axios.get.mockResolvedValue({status:200 , data:appointmentResponse.data})
axios.post.mockResolvedValue({status:200, data:{"status":200, "lines":1,"data":appointmentToAdd}})
//appointment is added for this date.
let checkDate=new Date(2021,11,27)
//show all two appointments
wrapper = mount(AppointmentsList, {store, localVue})
await flushPromises()
//verify two appointments in the store
expect(store.state.appointments.appointments).toHaveLength(2)
//find add button for first date
//click buttons open add/edit modal with date field prefilled
await wrapper.find('[data-cy="20211227"]').trigger('click')
const wrappedAddAppointment=wrapper.findComponent({name:'add-appointment'})
//check modal is opened with correct date
expect(wrappedAddAppointment.vm.$props.modalState).toBe(true)
expect(wrappedAddAppointment.vm.$data.appointment.appointment_at).toStrictEqual(checkDate)
//fill in fields and click submit
await wrappedAddAppointment.find('input[id="shortname"]').setValue(appointmentToAdd.short_name)
await wrappedAddAppointment.find('input[id="description"]').setValue(appointmentToAdd.desc)
await wrappedAddAppointment.find('input[id="teacher"]').setValue(1)
await wrappedAddAppointment.find('input[id="group"]').setValue(appointmentToAdd.group_id)
await wrappedAddAppointment.find('input[id="subject"]').setValue(appointmentToAdd.subject_id)
await wrappedAddAppointment.find('input[id="preferredtoa"]').setValue(appointmentToAdd.toa_preferred_id)
await wrappedAddAppointment.find('input[id="appointment_at"]').setValue(appointmentToAdd.appointment_at)
await wrappedAddAppointment.find('input[id="classhour"]').setValue(appointmentToAdd.classhour_id)
await wrappedAddAppointment.find('input[id="classroom"]').setValue(appointmentToAdd.classroom_id)
await wrappedAddAppointment.find('input[id="demo"]').setChecked(false)
await wrappedAddAppointment.find('button[name="save-button"]').trigger('click')
//check axios post(/appointments) is called
expect(axios.post).toHaveBeenCalledTimes(1)
expect(axios.post.mock.calls[0][0]).toContain('/appointments')
expect(axios.get).toHaveBeenCalledTimes(2)
//verify the modal is closed
expect(wrappedAddAppointment.vm.$props.modalState).toBe(false)
//verify the appointment is added to the store
expect(store.state.appointments.appointments).toHaveLength(3)
//verify added appointment is in list
expect(wrapper.text()).toContain(appointmentToAdd.short_name)
})
下一个测试测试点击行号。它还应打开 AddAppointment 模式,并预先填充日期和 class 小时(=行号)。由于添加约会已经过测试,测试停止。
test('clicking classhour adds appointment to list', async ()=>{
//setup ajax responses
axios.get.mockResolvedValue({status:200 , data:appointmentResponse.data})
axios.post.mockResolvedValue({status:200, data:{"status":200, "lines":1,"data":appointmentToAdd}})
let checkDate=new Date(2021,11,27)
console.log(store.state.appointments.appointments)
//show all appointments
const wrapper = mount(AppointmentsList, {store, localVue})
await flushPromises()
expect(axios.get).toHaveBeenCalledTimes(1)
--> expect(store.state.appointments.appointments).toHaveLength(2)
//test fails on line above. Added next two lines to check the appointments in the store and the return value of the axios call.
console.log(store.state.appointments.appointments) //shows three appointments with the last one being the appointment added in the previous test
console.log(axios.get.mock.results[0].value) //shows only two appointments returned as expected
//find classhour button of date 27-dec-2021 and classhour 3
await wrapper.find('[data-cy="20211227classhour3"]').trigger('click')
const wrappedAddAppointment=wrapper.findComponent({name:'add-appointment'})
//check modal is opened with correct date and classhour
expect(wrappedAddAppointment.vm.$props.modalState).toBe(true)
expect(wrappedAddAppointment.vm.$data.appointment.appointment_at).toStrictEqual(checkDate)
expect(wrappedAddAppointment.vm.$data.appointment.classhour.id).toBe(3)
})
此测试在标有箭头的行处失败。 Jest 报告的不是两次预约,而是三次在店内的预约。因为我在测试之间重建了商店,所以这对我来说毫无意义。添加的约会不应再在商店中。模拟电话在响应中清楚地显示了两个约会。但它变得更加陌生。我没有将两个初始约会添加到商店,而是决定将响应更改为 return 没有约会。我将模拟响应更改为:
axios.get.mockResolvedValue({status:200 , data:{status:200, lines:0, data:[]} })
和
的验证
expect(store.state.appointments.appointments).toHaveLength(0)
此测试通过,因此不会保留第一次测试中添加的约会。
谁能帮我解释一下?
编辑。根据评论中的要求添加了 appointmentModule。
const appointmentsmodule = {
state(){
return{
appointments:[],
appointmentPeriod:{
startdate:null,
enddate:null
},
subjectFilter:[],
}
},
mutations:{
storeAppointments(state, appointments){
//console.log('storeAppointments', appointments)
state.appointments = appointments
},
setAppointmentsPeriod(state, period){
//console.log('setAppointmentsPeriod', period)
state.appointmentPeriod = period
},
setSubjectFilter(state, filter){
//console.log('set Filter', filter)
state.subjectFilter= filter
},
addAppointment(state, appointmentToAdd){
state.appointments.push(appointmentToAdd)
}
},
getters:{
getFilteredAppointments: state=>{
//console.log('getting filtered appointments')
if(state.subjectFilter.length>0){
////console.log('filtered')
return state.appointments.filter(appointment=>{
/* //console.log(state.subjectFilter)
//console.log(appointment.subject_id)
//console.log(state.subjectFilter.includes(appointment.subject_id)) */
return state.subjectFilter.includes(appointment.subject_id)})
}
else{
////console.log('no filter')
return state.appointments
}
},
getAppointmentsPeriod(state){
//console.log('getting appointments period')
return state.appointmentPeriod
}
}
}
export default appointmentsmodule
我解决了这个问题。当存储来自后端的响应时,我用新数组替换了 vuex.state 中的数组,从而破坏了反应性。
当我使用
state.appointments.splice(0,Infinity, ...newAppointments)
原始数组的所有元素都被新元素替换。保留反应性并通过测试
我在测试项目的前端部分时遇到一个奇怪的问题。我在前端使用 Vue 组件。该项目是教师为助手设置约会的网站,因此助手可以为 class.
准备好一切首先让我解释一下结构。
我有一个按日期列出所有约会的组件。每个日期都是一张单独的卡片,一个日期的所有约会都是该卡片上的行。每行是一个特定的时隙。通过单击卡片顶部的按钮或单击行号,可以将约会添加到列表中。
所以我创建了三个组件:AppointmentsList.Vue 从后端获取约会并构建卡片列表,AppointmentsCard.Vue,它接收日期和该日期的所有约会作为道具,最后是 AppointmentRow.Vue,它在 table 的一行中显示约会的详细信息。状态、约会、请求的日期和其他数据保存在 Vuex 存储中。
我使用 TDD 构建项目,使用 Jest 和 Vue-test-utils 编写测试。 Mocks 用于模拟后端的响应。用卡片和行显示约会的测试工作正常。但是在测试按钮时遇到了一个奇怪的问题。
在下面的代码中,我向您展示了我的测试,为简洁起见进行了编辑。首先导入所有内容,然后定义后端对各种端点的响应。只有 appointmentResponse 很重要。请注意,有两个约会是 returned。
函数 createStore 从模块中构建一个商店。我将所有状态、吸气剂和突变保存在模块中。在每个测试 运行 之前,我创建一个新商店并使用商店的突变使用响应数据初始化商店。每次测试后,jest mocks 被清除,vue-test-utils 包装器被销毁。
*AppointmentList.spec.js*
import {mount, createLocalVue} from '@vue/test-utils'
import flushPromises from 'flush-promises'
import AppointmentsList from '../../resources/js/components/AppointmentsList.vue'
import axios from 'axios'
import Vuex from 'vuex'
import appointmentsmodule from '../../resources/js/storemodules/appointmentsModule.js'
import classhoursmodule from '../../resources/js/storemodules/classhoursModule.js'
import classroomsmodule from '../../resources/js/storemodules/classroomsModule.js'
import experimentsmodule from '../../resources/js/storemodules/experimentsModule.js'
import locationsmodule from '../../resources/js/storemodules/locationsModule.js'
import subjectsmodule from '../../resources/js/storemodules/subjectsModule.js'
import usersmodule from '../../resources/js/storemodules/usersModule.js'
import Vue from 'vue'
import { wrap } from 'lodash'
let appointmentResponse={"status":200,"lines":2,"data":[{"id":1,"subject_id":1,"owner_id":1,"group_id":1,"appointment_at":"2021-12-29T00:00:00.000000Z","classhour_id":1,"classroom_id":1,"experiment_id":null,"short_name":"magnam","description":"Sit cum quae quae quo quo consequatur.","demo":"0","toa_preferred_id":null,"location_id":1,"created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"},{"id":2,"subject_id":2,"owner_id":1,"group_id":1,"appointment_at":"2021-12-28T00:00:00.000000Z","classhour_id":1,"classroom_id":1,"experiment_id":null,"short_name":"possimus","description":"Velit eos sed esse reprehenderit.","demo":"0","toa_preferred_id":null,"location_id":1,"created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"}]}
let userResponse={"status":200,"lines":3,"data":[{"id":1,"code":"Est","name":"Mr. Americo Mertz I","email":"user1@hetstreek.nl","actual_location":"1","registrar":"1","email_verified_at":"2022-01-04T15:50:26.000000Z","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z","department_location_id":null,"welcome_valid_until":null,"roles":[{"id":2,"name":"toa","guard_name":"web","created_at":"2022-01-04T15:50:24.000000Z","updated_at":"2022-01-04T15:50:24.000000Z","pivot":{"model_id":"1","role_id":"2","model_type":"App\Models\User"}},{"id":1,"name":"beheerder","guard_name":"web","created_at":"2022-01-04T15:50:22.000000Z","updated_at":"2022-01-04T15:50:22.000000Z","pivot":{"model_id":"1","role_id":"1","model_type":"App\Models\User"}}]},{"id":2,"code":"Est","name":"Mr. Americo Mertz I","email":"user2@hetstreek.nl","actual_location":"2","registrar":"1","email_verified_at":"2022-01-04T15:50:26.000000Z","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z","department_location_id":null,"welcome_valid_until":null,"roles":[{"id":3,"name":"docent","guard_name":"web","created_at":"2022-01-04T15:50:25.000000Z","updated_at":"2022-01-04T15:50:25.000000Z","pivot":{"model_id":"2","role_id":"3","model_type":"App\Models\User"}}]},{"id":3,"code":"Est","name":"Mr. Americo Mertz I","email":"user3@hetstreek.nl","actual_location":"1","registrar":"1","email_verified_at":"2022-01-04T15:50:26.000000Z","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z","department_location_id":"{\"department_id\":1,\"location_id\":1,\"updated_at\":\"2022-01-04T15:50:26.000000Z\",\"created_at\":\"2022-01-04T15:50:26.000000Z\",\"id\":1}","welcome_valid_until":null,"roles":[{"id":4,"name":"leerling","guard_name":"web","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z","pivot":{"model_id":"3","role_id":"4","model_type":"App\Models\User"}}]}]}
let classhourResponse={"status":200,"lines":2,"data":[{"id":1,"name":"9","starttime":"15:22","endtime":"12:56","location_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"},{"id":3,"name":"1","starttime":"21:47","endtime":"20:16","location_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"}]}
let classroomResponse={"status":200,"lines":2,"data":[{"id":1,"name":"non","number":"756","in_use":"1","student_accessible":"0","teacher_accessible":"1","location_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"},{"id":3,"name":"ut","number":"214","in_use":"1","student_accessible":"0","teacher_accessible":"1","location_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"}]}
let experimentResponse={"status":200,"lines":2,"data":[{"id":1,"name":"nam","description":"Aliquam nihil voluptas aut vel neque.","student_selectable":"0","user_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"},{"id":3,"name":"similique","description":"Exercitationem officiis excepturi aut veniam voluptatum.","student_selectable":"1","user_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"}]}
let locationResponse={"status":200,"lines":2,"data":[{"id":1,"name":"Omnis.","school_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"},{"id":2,"name":"Illo.","school_id":"1","created_at":"2022-01-04T15:50:26.000000Z","updated_at":"2022-01-04T15:50:26.000000Z"}]}
let schoolResponse={"status":200,"lines":1,"data":[{"id":1,"schoolname":"Jaydon Mante","domain":"hetstreek.nl","max_locations":"1","payed_at":null,"due_date":null,"active":"1","storage_folder":"jaydonmante_hetstreeknl","created_at":"2022-01-05T14:41:49.000000Z","updated_at":"2022-01-05T14:41:49.000000Z"}]}
let subjectResponse={"status":200,"lines":2,"data":[{"id":1,"code":"SK","description":"Quis.","block_for_days":"9","color":"A3A3A3","created_at":"2022-01-05T18:54:41.000000Z","updated_at":"2022-01-05T18:54:41.000000Z"},{"id":2,"code":"re","description":"Libero.","block_for_days":"3","color":"A3A3A3","created_at":"2022-01-05T18:54:41.000000Z","updated_at":"2022-01-05T18:54:41.000000Z"}]}
function createStore(){
return {
modules:{
appointments:appointmentsmodule,
classhours:classhoursmodule,
classrooms:classroomsmodule,
experiments:experimentsmodule,
locations:locationsmodule,
subjects: subjectsmodule,
users: usersmodule
}
}
}
let wrapper
let store
beforeEach(()=>{
const newStore=createStore()
//define a new store for each test
store=new Vuex.Store(newStore)
//sets the range of dates for which the appointments need to be shown.
let startdate = new Date(2021, 11, 27)
let enddate = new Date(startdate)
enddate.setDate(startdate.getDate()+3)
//initialize the store
store.commit('setAppointmentsPeriod',{startdate,enddate})
store.commit('setSubjectFilter', [])
store.commit('storeAppointments',[])
store.commit('storeClasshours',classhourResponse.data)
store.commit('storeClassrooms',classroomResponse.data)
store.commit('storeExperiments',experimentResponse.data)
store.commit('storeLocations',locationResponse.data)
store.commit('storeSubjects',subjectResponse.data)
store.commit('storeUsers',userResponse.data)
//log to show the appointments are empty
console.log(store.state.appointments.appointments)
jest.clearAllMocks()
})
afterEach(()=>{
jest.clearAllMocks
wrapper.destroy()
})
jest.mock("axios")
const localVue =createLocalVue()
localVue.use(Vuex)
当组件 AppointmentsList 在测试中安装时,它会检测存储中的时间段变化,这会触发对后端的请求并加载该时间段的约会。
第一个测试测试卡片顶部的按钮。它触发按钮并验证添加约会的模式已打开,填写表单,触发提交按钮并验证数据是否已发送到后端,并且商店已更新,所以现在商店中应该有三个约会。这很好用。
test('add button adds appointment to list', async ()=>{
//set up the mock data.
let classhour=1
let appointmentToAdd={
id:3,
owner_id:1,
experiment_id:null,
short_name:"Schaduwpracticum",
description:"Lichtkastje met voeding, kartonnetje, scherm, liniaal",
group_id:1,
subject_id:1,
toa_preferred_id:1,
appointment_at:"2021-12-27",
classhour_id:classhour,
classroom_id:1,
demo:false,
location_id:1,
created_at:new Date(),
updated_at:new Date()
}
//mock axios responses. Get returns the appointments, post returns the added appointment.
axios.get.mockResolvedValue({status:200 , data:appointmentResponse.data})
axios.post.mockResolvedValue({status:200, data:{"status":200, "lines":1,"data":appointmentToAdd}})
//appointment is added for this date.
let checkDate=new Date(2021,11,27)
//show all two appointments
wrapper = mount(AppointmentsList, {store, localVue})
await flushPromises()
//verify two appointments in the store
expect(store.state.appointments.appointments).toHaveLength(2)
//find add button for first date
//click buttons open add/edit modal with date field prefilled
await wrapper.find('[data-cy="20211227"]').trigger('click')
const wrappedAddAppointment=wrapper.findComponent({name:'add-appointment'})
//check modal is opened with correct date
expect(wrappedAddAppointment.vm.$props.modalState).toBe(true)
expect(wrappedAddAppointment.vm.$data.appointment.appointment_at).toStrictEqual(checkDate)
//fill in fields and click submit
await wrappedAddAppointment.find('input[id="shortname"]').setValue(appointmentToAdd.short_name)
await wrappedAddAppointment.find('input[id="description"]').setValue(appointmentToAdd.desc)
await wrappedAddAppointment.find('input[id="teacher"]').setValue(1)
await wrappedAddAppointment.find('input[id="group"]').setValue(appointmentToAdd.group_id)
await wrappedAddAppointment.find('input[id="subject"]').setValue(appointmentToAdd.subject_id)
await wrappedAddAppointment.find('input[id="preferredtoa"]').setValue(appointmentToAdd.toa_preferred_id)
await wrappedAddAppointment.find('input[id="appointment_at"]').setValue(appointmentToAdd.appointment_at)
await wrappedAddAppointment.find('input[id="classhour"]').setValue(appointmentToAdd.classhour_id)
await wrappedAddAppointment.find('input[id="classroom"]').setValue(appointmentToAdd.classroom_id)
await wrappedAddAppointment.find('input[id="demo"]').setChecked(false)
await wrappedAddAppointment.find('button[name="save-button"]').trigger('click')
//check axios post(/appointments) is called
expect(axios.post).toHaveBeenCalledTimes(1)
expect(axios.post.mock.calls[0][0]).toContain('/appointments')
expect(axios.get).toHaveBeenCalledTimes(2)
//verify the modal is closed
expect(wrappedAddAppointment.vm.$props.modalState).toBe(false)
//verify the appointment is added to the store
expect(store.state.appointments.appointments).toHaveLength(3)
//verify added appointment is in list
expect(wrapper.text()).toContain(appointmentToAdd.short_name)
})
下一个测试测试点击行号。它还应打开 AddAppointment 模式,并预先填充日期和 class 小时(=行号)。由于添加约会已经过测试,测试停止。
test('clicking classhour adds appointment to list', async ()=>{
//setup ajax responses
axios.get.mockResolvedValue({status:200 , data:appointmentResponse.data})
axios.post.mockResolvedValue({status:200, data:{"status":200, "lines":1,"data":appointmentToAdd}})
let checkDate=new Date(2021,11,27)
console.log(store.state.appointments.appointments)
//show all appointments
const wrapper = mount(AppointmentsList, {store, localVue})
await flushPromises()
expect(axios.get).toHaveBeenCalledTimes(1)
--> expect(store.state.appointments.appointments).toHaveLength(2)
//test fails on line above. Added next two lines to check the appointments in the store and the return value of the axios call.
console.log(store.state.appointments.appointments) //shows three appointments with the last one being the appointment added in the previous test
console.log(axios.get.mock.results[0].value) //shows only two appointments returned as expected
//find classhour button of date 27-dec-2021 and classhour 3
await wrapper.find('[data-cy="20211227classhour3"]').trigger('click')
const wrappedAddAppointment=wrapper.findComponent({name:'add-appointment'})
//check modal is opened with correct date and classhour
expect(wrappedAddAppointment.vm.$props.modalState).toBe(true)
expect(wrappedAddAppointment.vm.$data.appointment.appointment_at).toStrictEqual(checkDate)
expect(wrappedAddAppointment.vm.$data.appointment.classhour.id).toBe(3)
})
此测试在标有箭头的行处失败。 Jest 报告的不是两次预约,而是三次在店内的预约。因为我在测试之间重建了商店,所以这对我来说毫无意义。添加的约会不应再在商店中。模拟电话在响应中清楚地显示了两个约会。但它变得更加陌生。我没有将两个初始约会添加到商店,而是决定将响应更改为 return 没有约会。我将模拟响应更改为:
axios.get.mockResolvedValue({status:200 , data:{status:200, lines:0, data:[]} })
和
的验证expect(store.state.appointments.appointments).toHaveLength(0)
此测试通过,因此不会保留第一次测试中添加的约会。 谁能帮我解释一下?
编辑。根据评论中的要求添加了 appointmentModule。
const appointmentsmodule = {
state(){
return{
appointments:[],
appointmentPeriod:{
startdate:null,
enddate:null
},
subjectFilter:[],
}
},
mutations:{
storeAppointments(state, appointments){
//console.log('storeAppointments', appointments)
state.appointments = appointments
},
setAppointmentsPeriod(state, period){
//console.log('setAppointmentsPeriod', period)
state.appointmentPeriod = period
},
setSubjectFilter(state, filter){
//console.log('set Filter', filter)
state.subjectFilter= filter
},
addAppointment(state, appointmentToAdd){
state.appointments.push(appointmentToAdd)
}
},
getters:{
getFilteredAppointments: state=>{
//console.log('getting filtered appointments')
if(state.subjectFilter.length>0){
////console.log('filtered')
return state.appointments.filter(appointment=>{
/* //console.log(state.subjectFilter)
//console.log(appointment.subject_id)
//console.log(state.subjectFilter.includes(appointment.subject_id)) */
return state.subjectFilter.includes(appointment.subject_id)})
}
else{
////console.log('no filter')
return state.appointments
}
},
getAppointmentsPeriod(state){
//console.log('getting appointments period')
return state.appointmentPeriod
}
}
}
export default appointmentsmodule
我解决了这个问题。当存储来自后端的响应时,我用新数组替换了 vuex.state 中的数组,从而破坏了反应性。 当我使用
state.appointments.splice(0,Infinity, ...newAppointments)
原始数组的所有元素都被新元素替换。保留反应性并通过测试