Javascript 分离;上下文“;及;“触发”;茉莉花

Javascript 分离;上下文“;及;“触发”;茉莉花,javascript,bdd,jasmine,mspec,jasmine-jquery,Javascript,Bdd,Jasmine,Mspec,Jasmine Jquery,假设我有一个简单的页面,点击页面标题可以切换页面内容的可见性(现实?不,但这是一个包含DOM元素的简单测试)。我将为您提供HTML和JS实现,因为我相信您可以在脑海中看到它 我正试图用jasmine来测试这一点,我遇到了代码重复问题,主要是围绕着上下文的分离(这个测试与Brethern测试有什么不同)和触发器(正在测试的操作和捕获结果) 如何将触发器(在本例中引发“click”)与上下文(shared=加载fixture,specific=隐藏页面内容)分开以避免代码重复 如果有帮助,下面是我在

假设我有一个简单的页面,点击页面标题可以切换页面内容的可见性(现实?不,但这是一个包含DOM元素的简单测试)。我将为您提供HTML和JS实现,因为我相信您可以在脑海中看到它

我正试图用jasmine来测试这一点,我遇到了代码重复问题,主要是围绕着上下文的分离(这个测试与Brethern测试有什么不同)和触发器(正在测试的操作和捕获结果)

如何将触发器(在本例中引发“click”)与上下文(shared=加载fixture,specific=隐藏页面内容)分开以避免代码重复

如果有帮助,下面是我在MSpec(一个.NET的上下文/规范框架)中要做的:


免责声明:我不是想在这里争论C#vs Javascript或MSpec vs任何东西,只是想提供一个我所追求的代码重用的例子。我还跳过了MSpec的一些功能,以保持示例的简单性。

这就是为什么可以嵌套descripe块,并且可以在仅适用于该级别和以下级别的任何级别调用
beforeach()

我不会太担心测试中的代码重复。更多的间接性意味着更少的可读性。在合理的范围内,测试中的冗长通常是一件好事。太多的特定宏函数只适用于代码的一小部分,最终会导致大量脆弱的测试,很少有人能想出如何更改

describe("Home", function () {
    describe("Selecting the page title", function () {
        beforeEach(function () {
            loadFixtures('Home.fixture.htm');
        });

        describe("when page content is visible", function () {
            beforeEach(function() {
                $('#pageTitle').trigger('click');
            });

            it("should hide page content", function () {
                expect($('#pageContent')).toBeHidden();
            });
        });

        describe("when page content is hidden", function () {
            beforeEach(function() {
                $('#pageContent').hide();
                $('#pageTitle').trigger('click');
            });

            it("should show page content", function () {
                expect($('#pageContent')).toBeVisible();
            });
        });
    });
});
或者,如果必须的话,在描述块中设置一个helper宏函数,但这可能会很快变得很糟糕。它开始在你的测试中加入太多的逻辑。最终你需要为你的测试做测试。老兄

describe("Home", function () {
    describe("Selecting the page title", function () {
        beforeEach(function () {
            this.loadHomeFixture = function(options) {
                options = options || {};
                loadFixtures('Home.fixture.htm');
                if (options.hidden) {
                    $('#pageContent').hide();
                }
            };
        });

        describe("when page content is visible", function () {
            it("should hide page content", function () {
                this.loadHomeFixture({ hidden: false });
                expect($('#pageContent')).toBeHidden();
            });
        });

        describe("when page content is hidden", function () {
            it("should show page content", function () {
                this.loadHomeFixture({ hidden: true });
                expect($('#pageContent')).toBeVisible();
            });
        });
    });
});
此外,有时我会先按状态组织测试,然后按功能组织测试。允许您更清晰地将初始状态与操作分离

以下是您如何以大纲的形式实现这一点

    • beforeach:加载主夹具
    • 当页面内容可见时
      • beforeach:使内容可见
      • 选择页面标题
        • beforeach:单击页面标题
        • 应该隐藏页面内容
          • 断言页面内容已隐藏
      • 当页面内容可见时执行的其他操作
    • 隐藏页面内容时
      • beforeach:隐藏内容
      • 选择页面标题
        • beforeach:单击页面标题
        • 应该显示页面内容
          • 断言页面内容可见
      • 隐藏页面内容时执行的其他操作
此结构允许您根据初始状态钻取,因此,如果您有更多依赖于页面上可见性的功能,那么测试这些功能就很容易了,因为您已经有了一个可以删除新测试的状态设置位置

虽然测试更像是一个矩阵(状态×特征),但大多数测试系统给我们一个树。您是否根据状态或功能从该树进行分支取决于您自己,并取决于哪个具有更高的复杂度

describe("Home", function () {
    describe("Selecting the page title", function () {
        beforeEach(function () {
            loadFixtures('Home.fixture.htm');
        });

        describe("when page content is visible", function () {
            beforeEach(function() {
                $('#pageTitle').trigger('click');
            });

            it("should hide page content", function () {
                expect($('#pageContent')).toBeHidden();
            });
        });

        describe("when page content is hidden", function () {
            beforeEach(function() {
                $('#pageContent').hide();
                $('#pageTitle').trigger('click');
            });

            it("should show page content", function () {
                expect($('#pageContent')).toBeVisible();
            });
        });
    });
});
describe("Home", function () {
    describe("Selecting the page title", function () {
        beforeEach(function () {
            this.loadHomeFixture = function(options) {
                options = options || {};
                loadFixtures('Home.fixture.htm');
                if (options.hidden) {
                    $('#pageContent').hide();
                }
            };
        });

        describe("when page content is visible", function () {
            it("should hide page content", function () {
                this.loadHomeFixture({ hidden: false });
                expect($('#pageContent')).toBeHidden();
            });
        });

        describe("when page content is hidden", function () {
            it("should show page content", function () {
                this.loadHomeFixture({ hidden: true });
                expect($('#pageContent')).toBeVisible();
            });
        });
    });
});