用C#在Visual Studio写Javascript单元测试(Firefox内核)
引用nuget包:
注意:Geckofx45 nuget包必须是最后引用,否则初始化会出错

编写JsRunner
using Gecko;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Way.UnitTest
{
class JsRunner:IDisposable
{
static JsRunner()
{
Xpcom.Initialize("Firefox");
} static List<JsFileReference> ReferenceConfigs = new List<JsFileReference>();
static List<string> GlobalJSFiles = new List<string>();
/// <summary>
/// 设置js文件依赖
/// </summary>
/// <param name="jsFile">js文件路径</param>
/// <param name="references">所依赖的js文件路径</param>
public static void SetJsReference(string jsFile,IEnumerable<string> references)
{
ReferenceConfigs.Add(new JsFileReference() {
JsFile = jsFile,
References = references,
});
}
/// <summary>
/// 添加全局js文件
/// </summary>
/// <param name="jsFile"></param>
public static void AddGlobalJsFile(string jsFile)
{
GlobalJSFiles.Add(jsFile);
} public JsRunner()
{
JsFiles.AddRange(GlobalJSFiles);
}
~JsRunner()
{
Dispose();
}
List<string> JsFiles = new List<string>();
/// <summary>
/// 添加js文件
/// </summary>
/// <param name="jsPath">js文件路径</param>
public void AddJsFile(string jsPath)
{
putJsFileContentToList(jsPath); } void putJsFileContentToList(string jsPath)
{
if (JsFiles.Contains(jsPath) )
{
return;
}
//查找该js是否引用其他js
var arr = ReferenceConfigs.Where(m => string.Equals(m.JsFile, jsPath, StringComparison.CurrentCultureIgnoreCase));
foreach( var item in arr )
{
foreach( var path in item.References )
{
putJsFileContentToList(path);
}
}
JsFiles.Add(jsPath);
} /// <summary>
/// 运行js代码
/// </summary>
/// <typeparam name="T">返回值类型</typeparam>
/// <param name="jsCode">一段js代码。如:return data.name;</param>
/// <param name="data">传到js里面的对象,js可以通过data.*直接使用此参数</param>
/// <returns></returns>
public T Run<T>(string jsCode, object data)
{
var gecko = new GeckoWebBrowser();
gecko.CreateControl();bool loadFinished = false; gecko.NavigationError += (s, e) =>
{
};
gecko.NSSError += (s, e) =>
{
};
gecko.DocumentCompleted += (s, e) => {
loadFinished = true;
};
string tempFileName = System.IO.Path.GetTempFileName();
System.IO.StreamWriter sw = new StreamWriter(System.IO.File.OpenWrite(tempFileName)); sw.WriteLine("<!DOCTYPE html>");
sw.WriteLine("<html>");
foreach (var path in JsFiles)
{
sw.WriteLine("<script src=\"file:///" + path + "\" type=\"text/javascript\"></script>");
}
sw.WriteLine("<body>"); sw.WriteLine("<input type=hidden id='inputResult'>");
sw.WriteLine("<input type=hidden id='inputError'>");
sw.WriteLine("</body>");
sw.WriteLine("</html>");
sw.Dispose(); gecko.Navigate("file:///" + tempFileName); while (!loadFinished)
{
System.Threading.Thread.Sleep();
System.Windows.Forms.Application.DoEvents();
}
System.IO.File.Delete(tempFileName); var jsContext = new AutoJSContext(gecko.Window); var js = @"
(function(d){
try{
var result = (function(data){" + jsCode + @"})(d);
return JSON.stringify(result);
}catch(e)
{
if(typeof e == 'string')
return JSON.stringify({ ______err : e , line:0 });
else
return JSON.stringify({ ______err : e.message , line:e.lineNumber });
}
})(" + (data == null ? "null" : Newtonsoft.Json.JsonConvert.SerializeObject(data)) + @");
"; string result;
jsContext.EvaluateScript(js, out result);
jsContext.Dispose();
gecko.Dispose(); if(result.StartsWith("{\"______err\":"))
{
var errObj = Newtonsoft.Json.JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(result);
string errMsg = errObj.Value<string>("______err");
//int lineNumber = errObj.Value<int>("line") - 3;
throw new Exception(errMsg);
}
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(result);
} /// <summary>
/// 读取js文件内容
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
string readJsFile(string filePath)
{
using (System.IO.FileStream fs = System.IO.File.OpenRead(filePath))
{
var data = new byte[fs.Length];
var isUtf8 = IsUTF8(fs);
fs.Position = ;
fs.Read(data, , data.Length);
if (isUtf8)
{
return Encoding.UTF8.GetString(data);
}
else
{
return Encoding.GetEncoding("gb2312").GetString(data);
}
}
} /// <summary>
/// 判断流是否是utf-8编码
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
static bool IsUTF8(Stream stream)
{
bool IsUTF8 = true; while (stream.Position < stream.Length)
{
byte b = (byte)stream.ReadByte();
if (b < 0x80) // (10000000): 值小于0x80的为ASCII字符
{ }
else if (b < (0xC0)) // (11000000): 值介于0x80与0xC0之间的为无效UTF-8字符
{
IsUTF8 = false;
break;
}
else if (b < (0xE0)) // (11100000): 此范围内为2字节UTF-8字符
{
if (stream.Position >= stream.Length - )
{
break;
}
byte nextByte = (byte)stream.ReadByte();
if ((nextByte & (0xC0)) != 0x80)
{
IsUTF8 = false;
break;
}
}
else if (b < (0xF0)) // (11110000): 此范围内为3字节UTF-8字符
{
if (stream.Position >= stream.Length - )
{
break;
} byte nextByte1 = (byte)stream.ReadByte();
byte nextByte2 = (byte)stream.ReadByte();
if ((nextByte1 & (0xC0)) != 0x80 || (nextByte2 & (0xC0)) != 0x80)
{
IsUTF8 = false;
break;
}
}
else
{
IsUTF8 = false;
break;
}
} return IsUTF8; } public void Dispose()
{
JsFiles.Clear();
}
} class JsFileReference
{
/// <summary>
/// js文件
/// </summary>
public string JsFile;
/// <summary>
/// 所依赖的js文件
/// </summary>
public IEnumerable<string> References; } }
编写单元测试基类
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Gecko; namespace Way.UnitTest.Javascript
{
/// <summary>
/// 其他js单元测试,建议继承此类
/// </summary>
[TestClass]
public class JSUnitTest
{ static JSUnitTest()
{
Xpcom.Initialize("Firefox"); //组合文件夹路径
var solutionPath = AppDomain.CurrentDomain.BaseDirectory + "\\..\\..\\..\\"; //添加全局使用的js文件
JsRunner.AddGlobalJsFile($"{solutionPath}\\js\\xpos-10.core.js"); //定义js文件的依赖关系,这里只是举例,文件实际不存在
//设置kkk.js依赖于a1.js a2.js 两个文件,
//这样,每当使用kkk.js文件,系统会自动引入a1.js a2.js 两个文件
JsRunner.SetJsReference($"{solutionPath}\\kkk.js", new string[] {
//这里写上所依赖js文件的路径
$"{solutionPath}\\a1.js",
$"{solutionPath}\\a2.js"
}); } }
}
编写测试代码
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Way.UnitTest.Javascript
{
[TestClass]
public class Example : JSUnitTest
{ [TestMethod]
public void Test()
{
using (JsRunner jsEngine = new JsRunner())
{
//编写js代码
var js = @"
return data.name;
";
//运行js代码
var result = jsEngine.Run<string>(js, new {name="JACK"});
if (result != "JACK")
throw new Exception("运算结果错误");
}
} }
}
用C#在Visual Studio写Javascript单元测试(Firefox内核)的更多相关文章
- 用C#在Visual Studio写Javascript单元测试
1.在vs创建一个标准的单元测试工程 2.引用nuget包:Edge.js 我是用的是6.11.2版本 3.编写JsRunner类 using EdgeJs; using System; using ...
- Visual Studio 中的单元测试 UNIT TEST
原文:Visual Studio 中的单元测试 UNIT TEST 注:本文系作者原创,可随意转载,但请注明出处.如实在不愿注明可留空,强烈反对更改原创出处.TDD(Test-Driven Devel ...
- 基于Visual Studio .NET2015的单元测试
基于Visual Studio .NET2015的单元测试 1. 在Visual Studio .NET2015中创建任意项目. 2. 在某个公共类的公共方法的名称上面点击右键,选择“创建 ...
- 基于Visual Studio .NET2015的单元测试 OpenCover
https://www.cnblogs.com/XiaoRuLiang/p/10095723.html 基于Visual Studio .NET2015的单元测试 1. 在Visual Stud ...
- Visual Studio中UnitTesting单元测试模板代码生成
在软件研发过程中,单元测试的重要性直接影响软件质量.经验表明一个尽责的单元测试方法将会在软件开发的某个阶段发现很多的Bug,并且修改它们的成本也很低.在软件开发的后期阶段,Bug的发 ...
- Visual Studio 2013进行单元测试
使用Visual Studio 2013进行单元测试--初级篇 1.打开VS2013 --> 新建一个项目.这里我们默认创建一个控制台项目.取名为UnitTestDemo 2.在解决方案里面 ...
- 使用Visual Studio 2013进行单元测试
使用Visual Studio 2013进行单元测试 1.打开VS2013 --> 新建一个项目.这里我们默认创建一个控制台项目.取名为UnitTestDemo 2.在解决方案里面新增一个单元测 ...
- Visual Studio(VS)C++单元测试
版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:Visual Studio(VS)C++单元测试 本文地址:http://techie ...
- 1,[VS入门教程] 使用Visual Studio写c语言 入门与技巧精品文~~~~下载安装篇
Microsoft Visual Studio是微软(俗称巨硬)公司出品的强大IDE(Integrated Development Environment 集成开发环境),功能强大齐全,界面舒服之类的 ...
随机推荐
- [Vue-rx] Cache Remote Data Requests with RxJS and Vue.js
A Promise invokes a function which stores a value that will be passed to a callback. So when you wra ...
- 喜欢玩warcraft的ltl
喜欢玩warcraft的ltl 时间限制:2000 ms | 内存限制:65535 KB 难度:3 描写叙述 ltl 很喜欢玩warcraft.由于warcraft十分讲究团队总体实力,而他自己如 ...
- 面向对象设计:共性VS个性-------继承的粒度和聚合的粒度以及类的重构
共性和个性 依据面向对象的原理.类是对象的抽象.也就是说.类是一系列的既有共性又有个性的对象的高度概括,类的属性和方法代表了隶属于该类的全部对象的共性,类的每一个对象实例都能够有不同的属性值,这反映了 ...
- LeetCode 645. Set Mismatch (集合不匹配)
The set S originally contains numbers from 1 to n. But unfortunately, due to the data error, one of ...
- MySQL 登录问题
1.问题一:使用update mysql.user set password='root'改动密码后,不能登录 解决:操作过程例如以下. (1)关闭mysql(杀掉mysqld进程),然后使用命令: ...
- js将图片转为base64编码,以字符串传到后台存入数据库
(前台在中approve_edit.html中,后台不变) 链接参考:http://www.cnblogs.com/Strom-HYL/p/6782176.html 该链接文中并没有用到easyUI的 ...
- android后台input命令模拟按键【转】
本文转载自:http://www.cnblogs.com/sh1o2os/archive/2013/02/05/2893201.html 有时做开发时,我们使用的触摸屏没有虚拟按键(HOME.BACK ...
- [NOIP2003普及组]麦森数(快速幂+高精度)
[NOIP2003普及组]麦森数(快速幂+高精度) Description 形如2^P-1的素数称为麦森数,这时P一定也是个素数.但反过来不一定,即如果P是个素数,2^P-1不一定也是素数.到1998 ...
- 怎么看待MYSQL的性能
MySQL在单实例性能方面和Oracle相比还有一些差距,我们通过规范和技术手段来降低这些性能差距带来的问题. 首先,大量甚至海量数据的增删改.查询.聚合查询的性能还有待提高.为了规避这些问题,我们在 ...
- poj3469 Dual Core CPU——最小割
题目:http://poj.org/problem?id=3469 最小割水题(竟然没能1A): 代码如下: #include<iostream> #include<cstdio&g ...