【问题标题】:Vue Component Unit TestingVue 组件单元测试
【发布时间】:2019-04-18 01:23:04
【问题描述】:

我有一个计数器组件 - 非常简单。

<div> + </div>
    <input type="text" v-model="countData" />
<div> - </div>

详细代码在这里-https://github.com/Shreerang/Vue-Nuggets/blob/master/src/components/QuantitySelector/QuantitySelector.vue

我正在尝试对这个组件进行单元测试。

it('Renders a default quantity selector with the max count set to 6', () => {
    const wrapper = shallowMount(QuantitySelector);
    wrapper.find('input[type="text"]').setValue('1');
    expect(wrapper.find('input[type="text"]').element.value).toBe('1'); // notice here that the value is a String, whereas I expect it to be a Number

    wrapper.findAll('div').at(2).trigger('click');
    expect(wrapper.vm.countData).toBe('2'); // This fails as countData becomes "11" instead of doing a 1 + 1 = 2 and then becoming Number 2.

    expect(wrapper.find('input[type="text"]').element.value).toBe(2); // You can notice the same thing as above.

    wrapper.find('input[type="text"]').setValue(wrapper.vm.countData); // Do I have to do this? This does not seem right to me :frowning:
});

我无法让这个单元测试工作!对此的任何帮助表示赞赏!

【问题讨论】:

    标签: unit-testing vue.js vuejs2 jestjs vue-component


    【解决方案1】:

    文本字段包含文本值。请注意,您甚至指定了一个文本值:setValue('1')。如果您手动更改输入中的值(例如,更改为 3)并按下增量按钮,它会变为 31。您的测试告诉您真相。

    您需要更改函数以转换为数字。 [更新] 正如你的评论告诉我的那样,Vue 有一个 .number 修饰符用于 v-model 用于此目的

    new Vue({
      el: '#app',
    	name: 'quantity-selector',
    	props: {
    		count: {
    			type: Number,
    			default: 1,
    		}, // Makes sense to have default product count value
    		maxCount: {
    			type: Number,
    			default: 6,
    		}, // maxCount makes sense when you have a restriction on the max quantity for a product
    		iconDimensions: {
    			type: Number,
    			default: 15,
    		},
    		minusIconFillColor: {
    			type: String,
    			default: '#000',
    		},
    		plusIconFillColor: {
    			type: String,
    			default: '#000',
    		},
    		isCountEditable: {
    			type: Boolean,
    			default: true,
    		},
    	},
    	data() {
    		return {
    			countData: this.count,
    		};
    	},
    	computed: {
    		minusIconColor: function() {
    			return this.countData === this.count ? '#CCC' : this.minusIconFillColor;
    		},
    		plusIconColor: function() {
    			return this.countData === this.maxCount ? '#CCC' : this.plusIconFillColor;
    		},
    	},
    	methods: {
    		decrement: function() {
    			if (this.countData > this.count) {
    				this.countData -= 1;
    			}
    		},
    		increment: function() {
    			if (this.countData < this.maxCount) {
    				this.countData += 1;
    			}
    		},
    		adjustCount: function() {
    			if (this.countData > this.maxCount) {
    				this.countData = this.maxCount;
    			} else if (this.countData < this.count) {
    				this.countData = this.count;
    			} else {
    				if (isNaN(Number(this.countData))) {
    					this.countData = this.count;
    				}
    			}
    		},
    	}
    });
    .nugget-quantity-counter {
    	display: inline-flex;
    }
    .nugget-quantity-counter div:first-child {
    	border: solid 1px #ccc;
    	border-radius: 5px 0px 0px 5px;
    }
    .nugget-quantity-counter div:nth-child(2) {
    	border-top: solid 1px #ccc;
    	border-bottom: solid 1px #ccc;
    	display: flex;
    	flex-direction: column;
    	justify-content: center;
    }
    .nugget-quantity-counter input[type='text'] {
    	border-top: solid 1px #ccc;
    	border-bottom: solid 1px #ccc;
    	border-left: none;
    	border-right: none;
    	text-align: center;
    	width: 20px;
    	padding: 12px;
    	margin: 0;
    	font-size: 16px;
    }
    .nugget-quantity-counter div:last-child {
    	border: solid 1px #ccc;
    	border-radius: 0px 5px 5px 0px;
    }
    .nugget-quantity-counter > div {
    	cursor: pointer;
    	padding: 12px;
    	width: 20px;
    	text-align: center;
    }
    .nugget-quantity-counter > div > svg {
    	height: 100%;
    }
    <script src="https://unpkg.com/vue@latest/dist/vue.js"></script>
    <div id="app">
      <div @click="decrement">
        <svg viewBox="0 0 24 24" :width="iconDimensions" :height="iconDimensions">
                    <g>
                        <path d='M64 0 M2 11 L2 13 L22 13 L22 11 Z' :fill="minusIconColor" />
                    </g>
                </svg>
      </div>
      <input v-if="isCountEditable" type="text" v-model.number="countData" @blur="adjustCount" />
      <div v-else>{{countData}}</div>
      <div @click="increment">
        <svg viewBox="0 0 24 24" :width="iconDimensions" :height="iconDimensions">
                    <g>
                        <path d="M 11 2 L 11 11 L 2 11 L 2 13 L 11 13 L 11 22 L 13 22 L 13 13 L 22 13 L 22 11 L 13 11 L 13 2 Z" :fill="plusIconColor" />
                    </g>
                </svg>
      </div>
    </div>

    【讨论】:

    • 谢谢罗伊。我已经想通了。数字和文本字符串的东西。令我困惑的是,组件如何按预期工作。见这里 - ecommerce-vue-nuggets.herokuapp.com
    • 按照我的描述工作:手动将第一个输入的值设置为 3,然后单击加号。你有 31 在空白处。你从一个数字值开始,但是当文本项中的值发生变化时,它就变成了一个文本值。
    • 嗨,罗伊。尽管您的回答为我指明了正确的方向,但我认为我们不需要在任何地方添加 Number(this.countData) 。相反,我们可以 &lt;input type="text" v-model.number="countData" /&gt; .number 修饰符 v-model 将解决这个问题。
    • 我已经更新了我的答案,这当然是一个更好的选择。感谢您分享这些信息!
    • 我也在 Vue 论坛上得到了这个答案!感谢 LinusBorg - forum.vuejs.org/t/vue-unit-test/48709/2
    猜你喜欢
    • 2020-12-02
    • 2019-01-02
    • 1970-01-01
    • 2020-10-16
    • 2023-01-03
    • 2019-12-25
    • 2019-03-20
    • 2020-08-02
    • 1970-01-01
    相关资源
    最近更新 更多