【问题标题】:Generating JavaScript in C# and subsequent testing在 C# 中生成 JavaScript 和后续测试
【发布时间】:2023-03-30 22:44:01
【问题描述】:

我们目前正在开发一个 ASP.NET MVC 应用程序,它大量使用基于属性的元数据来驱动 JavaScript 的生成。

下面是我们正在编写的方法类型的示例:

function string GetJavascript<T>(string javascriptPresentationFunctionName,
                                 string inputId,
                                 T model)
{
    return @"function updateFormInputs(value){
        $('#" + inputId + @"_SelectedItemState').val(value);
        $('#" + inputId + @"_Presentation').val(value);
     }

    function clearInputs(){
        " + helper.ClearHiddenInputs<T>(model) + @"
        updateFormInputs('');
    }

    function handleJson(json){
        clearInputs();
        " + helper.UpdateHiddenInputsWithJson<T>("json", model) + @"
        updateFormInputs(" + javascriptPresentationFunctionName + @"());
        " + model.GetCallBackFunctionForJavascript("json") + @"
    }";
}

此方法会生成一些样板,并移交给返回字符串的各种其他方法。然后将整个批次作为字符串返回并写入输出。

我的问题是:

1) 除了使用大字符串块之外,还有更好的方法吗?

我们考虑过使用 StringBuilder 或响应流,但它看起来很“吵”。使用 string.format 开始变得难以理解。

2) 你将如何对这段代码进行单元测试?只是进行字符串比较以查找字符串中的特定输出,这似乎有点业余。

3) 实际测试最终的 JavaScript 输出怎么样?

感谢您的意见!

【问题讨论】:

    标签: c# javascript asp.net-mvc unit-testing code-generation


    【解决方案1】:

    我们也在项目中进行了大量的 JS 生成,我们正在使用 StringBuilder 来完成。

    StringBuilder sb = new StringBuilder();
    
    sb.Append("some javascript stuff")
      .Append("some more")
      .AppendFormat("formatted stuff {0}", "here");
    
    return sb.ToString();
    

    它不漂亮,但没有解决方案。

    关于测试,我们实际上并没有对生成的代码进行任何单元测试。在发布之前,人们会去测试所有功能,以确保它们按预期工作。

    【讨论】:

    • 好的,那么您正在通过浏览器 UI 进行集成测试?
    • 我想是的。 :P 实际上,所有主要的浏览器。 IE6 对 ExtJS 来说太痛苦了。
    【解决方案2】:

    如果您不关心超级骗子的性能,您可以使用模板语言来生成 javascript。

    然后,对于单元测试,您只需使用适当的绑定/变量填充模板,然后通过 Javascript 评估器(如 Rhino)或任何 .NET 等价物运行它,以至少测试语法(如果不是实际的 JS 代码)。

    除此之外,我会严重质疑生成这样的 Javascript 的软件的设计。看起来您正在使用 JQuery,但直接引用 $ 可能会导致一些问题。

    如果编译器生成 Javascript 是一回事(ala GWT),但我会尽可能地将您的客户端 JS 代码与您的 .NET 代码分开(更不用说您的 .NET 代码看起来像服务器端 JS 谈论的混乱)。

    这种将客户端与服务器分离的流行设计被称为 SOFEA。我让你用谷歌搜索。

    【讨论】:

    • 可能值得一提的是,我们正在使用 MVCContrib 和 InputBuilders。有问题的模型是 ViewModel。我们使用 AutoMapper 将视图与域模型分开。问题是我们有一些常见的例程需要在 JavaScript 中设置 ViewModel 的属性。
    【解决方案3】:

    我通常会尝试为我的大部分/全部 javascript 代码创建一个单独的 .js 文件。通常我需要对许多由 ASP 控件或服务器端代码动态创建的元素应用 common bahvior,因此我可能无法将所有内容都编码到 .js 文件中。

    我发现您想要在服务器上生成 javascript 的主要原因是因为在页面呈现之前您不会知道元素的 ID。因此,我尝试尽可能地压缩这种依赖关系,以便生成尽可能少的 javascript。例如,在传统的 ASP.Net(不是 MVC)中,如果我要渲染一组表单,例如示例中的表单,每个表单都有多个字段,那么我可能会在后面的代码中包含如下内容:

    protected void FormRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
    {
        Control form = e.Item.FindControl("MyForm");
        ClientScript.RegisterStartupScript(this.GetType(), "prepareForm_" + form.ClientID, @"prepareForm('" + form.ClientID + "');", true);
    }
    

    一个单独的 .js 文件将包含 prepareForm 函数的定义,如下所示:

    // define a formPresenter "class" that encapsulates the behavior for a given form
    function formPresenter(formId) {
    
        this.setFirstName = function(value) {
            $("#" + formId + "_FirstName").val(value);
        }
    
        this.setLastName = function(value) {
            $("#" + formId + "_LastName").val(value);
        }
    
        // create other functions to handle more complicated logic
    
        // clear fields
        this.clearInputs = function() {
            this.setFirstName("");
            this.setLastName("");
            //...
        }
    
        // receive Json object
        this.handleJson = function(json) {
            this.clearInputs();
    
            // populate fields with json object
            this.setFirstName(json.FirstName);
            this.setLastName(json.LastName);
            //...
        }
    
        // "constructor" logic
    }
    
    function prepareForm(formId) {
        // create a new formPresenter object and shove it onto the specified element as the "presenter"
        document.getElementById(formId).presenter = new formPresenter(formId);
    }
    

    现在几乎所有的实际逻辑都在它自己的 .js 文件中,应该更容易维护。如果您需要访问给定表单的 formPresenter 对象,那么您只需要获取对 formId 参数引用的任何元素的引用并访问 presenter 变量:

    "document.getElementById(" + form.ClientID + ").presenter.handleJson(json);"
    

    注意:由于我一直在使用 JQuery,因此我发现甚至不需要包含服务器生成的任何 javascript。通常,我可以通过查找特定的 CSS 类名(或类似的名称)找到我需要的元素,并执行我需要的任何设置/初始化。

    【讨论】:

    • 可能是我正在寻找的最接近的结果,感谢您的意见。
    【解决方案4】:

    我们创建了一个库,专门用于将 JavaScript 以类似流利的语法嵌入到我们的 C# 代码中,然后将其开源。

    看看Adam.JSGenerator

    【讨论】:

    • 这是旧的,但它非常适合我们需要的东西(因此 +1)。我也非常非常喜欢它与 VS 2015 开箱即用的编译方式。非常感谢!
    • 嘿,没问题!我知道我有一段时间没有更新这个库了,主要是因为需求消失了一点,但如果你觉得有什么缺失,请随时告诉我。也许我应该把整个东西移到 GitHub...
    • 如果你有时间,GitHub 可能是个不错的选择。该库经过了很好的测试和记录,目前还没有问题......我希望其他人了解它!这是一种更好的方法,例如,构建服务器端基于字符串的 js...
    • 我认为我遇到的最大问题是它是一个 Mercurial 存储库,我在将旧的 Mercurial 存储库迁移到 git 时遇到了麻烦。
    • LOL 从头开始​​。 GitHub 现在有一个 Mercurial 导入器。我现在会调查一下。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-31
    相关资源
    最近更新 更多