简体   繁体   English

如何使用 Jasmine 对 Knockout.js 订阅功能进行单元测试?

[英]How can I unit test Knockout.js subscribe functions with Jasmine?

I have a knockout viewmodel which has a few subscribe functions that update some default selections after it picks up a change to a parent selection.我有一个淘汰赛视图模型,它有一些订阅功能,在它获取对父选择的更改后更新一些默认选择。

But I'm not sure how to call the function and fake the dependencies in order to test it.但我不确定如何调用该函数并伪造依赖项以对其进行测试。

This code snippit is similar to the function but not the exact one.此代码片段与函数相似,但不完全相同。 It's subscribed to SelectedParentOption observable and when it changes to checks through what's available and selects a new value.它订阅了 SelectedParentOption observable,当它更改为检查可用内容并选择新值时。

        //When selection changes select default for remaining selection
        self.ServerModel.ProductModel.SelectedParentOption.subscribe(function () {
            // Loop through options returned by function checking for correct key
            self.availableOptions().forEach(function (option) {
                if (option.Key == 1)
                    // Assign default selection
                    self.ServerModel.ProductModel.SelectedChildOption(option);
            }
        });

This is the Jasmine unit test这是 Jasmine 单元测试

        it("Selection set to default after change", function () {
        // Spy on available options to dictate what's available
        spyOn(testModel, 'availableOptions').and.returnValue(TestOptions);
        // Change selection
        ServerModel.ProductModel.SelectedParentOption(Option1);
        // Call function
        ServerModel.ProductModel.SelectedParentOption.subscribe();
        // Verify default was selected
        expect(ServerModel.ProductModel.SelectedChildOption()).toBe(ExpectedOption);
    });

Currently this changes the option but the subscribe code doesn't seem to get hit at all.目前这改变了选项,但订阅代码似乎根本没有受到影响。

I don't really see anything wrong with your code, other that you don't need to call subscribe() after setting an observable's value;我真的看不出你的代码有什么问题,除了在设置 observable 的值后你不需要调用subscribe() knockout does that for you.淘汰赛为您做到这一点。 The subscribe method is used to register a function that handles a new value. subscribe 方法用于注册一个处理新值的函数。

It's important to keep in mind however, that subscriptions won't be triggered if the observable is set to the same value as it already holds.然而,重要的是要记住,如果 observable 设置为与它已经拥有的值相同的值,则不会触发订阅。

So in your case: if SelectedParentOption() === Option1 before your Change selection comment, no reset will occur.所以在您的情况下:如果SelectedParentOption() === Option1在您的Change selection评论之前,则不会发生重置。 If you want to manually trigger it, you use valueHasMutated() (instead of where you used subscribe() )如果你想手动触发它,你使用valueHasMutated() (而不是你使用subscribe()

Check out these (quick and dirty) tests.检查这些(快速和肮脏的)测试。

Test 2 and 3 contradict each other;测试 2 和 3 相互矛盾; only 1 of them will pass.只有 1 个会通过。 They describe different behavior:它们描述了不同的行为:

  • 2: only reset if the parent selection is actually changed 2:仅在父选择实际更改时重置
  • 3: always reset when somebody sets parent selection, even if it's the same value it was before 3:当有人设置父选择时总是重置,即使它与之前的值相同

I can imagine test 2 better describes what you're looking for, but if you want test 3 to pass, you can extend your observable to always notify its subscribers .我可以想象测试 2 更好地描述了您正在寻找的内容,但是如果您希望测试 3 通过,您可以扩展您的 observable 以始终通知其订阅者 Uncomment the comment in the ViewModel to see the difference.取消注释 ViewModel 中的注释以查看差异。

 function ViewModel() { this.selectedParent = ko.observable(0)/*.extend({notify: 'always'})*/; this.children = [{ key: 0 }, { key: 1}, { key: 2 }]; this.selectedChild = ko.observable(null); this.selectedParent.subscribe(function() { this.selectedChild(this.children.find(function(child) { return child.key === 1; })); }, this) } describe('Subscription test', function() { var vm; beforeEach(function() { vm = new ViewModel(); }); it("should reset child selection to child 1 when parent changes", function() { expect(vm.selectedChild()).toBe(null); vm.selectedParent(1); expect(vm.selectedChild().key).toBe(1); }); it("should not reset child when re-setting the same parent selection", function() { vm.selectedParent(1); vm.selectedChild(vm.children[2]); expect(vm.selectedChild().key).toBe(2); // Same primitive, won't trigger the subscription vm.selectedParent(1); expect(vm.selectedChild().key).toBe(2); }); // To fix this test, uncomment .extend in selectedParent it("should always reset child when re-setting the same parent selection", function() { vm.selectedParent(1); vm.selectedChild(vm.children[2]); expect(vm.selectedChild().key).toBe(2); vm.selectedParent(1); expect(vm.selectedChild().key).toBe(1); }); it("should initialize without a selection", function() { expect(vm.selectedChild()).toBe(null); }); it("should set selections", function() { vm.selectedChild(vm.children[2]); expect(vm.selectedChild().key).toBe(2); }); });
 <script src="http://searls.github.io/jasmine-all/jasmine-all-min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

You should call .subscribe() before you change the observed value.您应该在更改观察值之前调用.subscribe() It only detects changes after subscription has been set up.它仅在设置订阅检测更改。 Try this instead:试试这个:

// enable subscription ServerModel.ProductModel.SelectedParentOption.subscribe(); // Change selection ServerModel.ProductModel.SelectedParentOption(Option1); // Verify default was selected expect(ServerModel.ProductModel.SelectedChildOption()).toBe(ExpectedOption);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM