在C#中生成JavaScript并进行后续测试

时间:2010-04-13 10:44:16

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

我们目前正在开发一个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或Response Stream,但它看起来非常“吵”。使用string.format开始变得难以理解。

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

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

感谢您的投入!

4 个答案:

答案 0 :(得分:2)

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

查看Adam.JSGenerator

答案 1 :(得分:1)

我们在项目中也做了很多JS生成,我们正在使用StringBuilder来完成它。

StringBuilder sb = new StringBuilder();

sb.Append("some javascript stuff")
  .Append("some more")
  .AppendFormat("formatted stuff {0}", "here");

return sb.ToString();

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

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

答案 2 :(得分:1)

如果您不关心超级演奏者表现,可以使用模板语言来生成javascript。

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

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

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

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

答案 3 :(得分:1)

我通常会尝试为大部分/全部javascript代码创建单独的.js文件。通常我需要将常见的bahvior应用于由ASP控件或服务器端代码动态创建的许多元素,因此我可能无法将所有内容编码为.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类名称(或者那种效果)找到我需要的元素,并执行我需要的任何设置/初始化。