xunit-to-nunit
Version:
Converts C# XUnit tests to NUnit tests
328 lines (272 loc) • 8.13 kB
JavaScript
var log = require('color-log');
var fs = require('fs');
var path = require('path');
var glob = require('glob');
String.prototype.replaceAll = function (search, replacement) {
var target = this;
return target.replace(new RegExp(search, 'g'), replacement);
}
String.prototype.insertAt = function (index, string) {
return this.substr(0, index) + string + this.substr(index);
}
module.exports.examples = [{
xunit: `using Xunit;
using Xunit.Abstractions;
public class SomeTests
{
[Fact]
public void Test1()
{
string testString = "avocado";
Assert.NotEmpty(testString);
Assert.Equal("avocado", testString);
Assert.NotEqual("potato", testString);
}
[Theory]
[InlineData(5, 15)]
[InlineData(6, 18)]
[InlineData(7, 21)]
public void Test2(int number1, int number2)
{
var result = number2 / number1;
Assert.Equal(3, result);
}
}`,
nunit: `using NUnit.Framework;
[TestFixture]
public class SomeTests
{
[Test]
public void Test1()
{
string testString = "avocado";
Assert.IsNotEmpty(testString);
Assert.AreEqual("avocado", testString);
Assert.AreNotEqual("potato", testString);
}
[Test]
[TestCase(5, 15)]
[TestCase(6, 18)]
[TestCase(7, 21)]
public void Test2(int number1, int number2)
{
var result = number2 / number1;
Assert.AreEqual(3, result);
}
}`
}];
// Assert calls to be converted are listed here
var assertsList = [{
XUnitAssert: 'Equal',
NUnitAssert: 'AreEqual'
},
{
XUnitAssert: 'NotEqual',
NUnitAssert: 'AreNotEqual'
},
{
XUnitAssert: 'Same',
NUnitAssert: 'AreSame'
},
{
XUnitAssert: 'NotSame',
NUnitAssert: 'AreNotSame'
},
{
XUnitAssert: 'Empty',
NUnitAssert: 'IsEmpty'
},
{
XUnitAssert: 'NotEmpty',
NUnitAssert: 'IsNotEmpty'
},
{
XUnitAssert: 'IsType',
NUnitAssert: 'IsInstanceOfType'
},
{
XUnitAssert: 'IsNotType',
NUnitAssert: 'IsNotInstanceOfType'
},
{
XUnitAssert: 'Null',
NUnitAssert: 'IsNull'
},
{
XUnitAssert: 'NotNull',
NUnitAssert: 'IsNotNull'
},
{
XUnitAssert: 'True',
NUnitAssert: 'IsTrue'
},
{
XUnitAssert: 'False',
NUnitAssert: 'IsFalse'
},
];
// Other differences between XUnit and NUnit that should be converted
var otherSyntaxDifferenceList = [{
XUnitSyntax: ['using Xunit;'],
NUnitSyntax: 'using NUnit.Framework;'
},
{
XUnitSyntax: ['[Fact]', '[Fact()]', '[Theory]', '[Theory()]'],
NUnitSyntax: '[Test]'
},
{
XUnitSyntax: ['InlineData('],
NUnitSyntax: 'TestCase('
},
{
XUnitSyntax: ['ClassData('],
NUnitSyntax: 'TestCaseSource('
},
{
XUnitSyntax: ['Assert.Contains("'],
NUnitSyntax: 'StringAssert.Contains("'
},
];
// Converts Assert statements for each Assert type in assertsList (line 1)
function convertLineAssert(line) {
for (var i = 0; i < assertsList.length; i++) {
var x = 'Assert.' + assertsList[i].XUnitAssert + '(';
var n = 'Assert.' + assertsList[i].NUnitAssert + '(';
line = line.replace(x, n);
}
return line;
}
// Adds [TestFixture] above classes named {name}Facts or {name}Tests
function addTestFixtureAttributes(lines) {
for (var i = 0; i < lines.length; i++) {
if (/^( *public *class \w+(Facts|Tests) *)/.test(lines[i])) {
if (!(i > 0 && lines[i - 1].indexOf('[TestFixture]') > -1)) {
var spaces = '';
for (var j = 0; j < lines[i].search(/\S/); j++) spaces += ' ';
lines[i] = spaces + '[TestFixture]\n' + lines[i];
}
}
}
return lines;
}
// Remove line from list of lines if matches search string
function removeLineFromList(linesList, searchString) {
for (var i = linesList.length - 1; i--;) {
if (linesList[i].indexOf(searchString) > -1) linesList.splice(i, 1);
}
return linesList;
}
// Convert line from XUnit syntax to NUnit syntax, including Assert statements
module.exports.convertLine = function (line) {
line = convertLineAssert(line);
for (var i = 0; i < otherSyntaxDifferenceList.length; i++) {
for (var j = 0; j < otherSyntaxDifferenceList[i].XUnitSyntax.length; j++) {
var x = otherSyntaxDifferenceList[i].XUnitSyntax[j];
var n = otherSyntaxDifferenceList[i].NUnitSyntax;
line = line.replace(x, n);
}
}
return line;
};
// Main method to convert code
module.exports.convertCode = function (codeIn) {
// Split code into list, breaking at newlines
var codeLines = codeIn.split('\n');
for (var i = 0; i < codeLines.length; i++) {
codeLines[i] = module.exports.convertLine(codeLines[i]);
}
codeLines = addTestFixtureAttributes(codeLines);
codeLines = removeLineFromList(codeLines, 'using Xunit.Abstractions;');
// Join list of lines to form newline seperated string
var codeOut = codeLines.join('\n');
return codeOut;
};
function appendFilepath(filepath, append) {
if (!filepath || !append) return;
var index = filepath.indexOf('.cs');
var result = filepath.insertAt(index, append);
return result;
}
// Method to convert test in file
module.exports.convertFile = function (source, destination, verbose, overwrite, append) {
if (verbose == null) verbose = true;
if (source == destination) {
if (overwrite) append = null;
// If something should be appended to filename
if (append != null) {
destination = appendFilepath(destination, append);
} else {
if (!overwrite) {
throw new Error("overwrite is false, and no `append` parameter specified.");
}
}
}
var data = '';
try {
data = fs.readFileSync(source, 'utf-8');
} catch (e) {
log.error('Error loading file: ' + source);
throw e;
}
var converted = '';
try {
converted = module.exports.convertCode(data);
} catch (e) {
log.error('Error converting test');
throw e;
}
try {
fs.writeFileSync(destination, converted, 'utf-8');
} catch (e) {
log.error('Error writing file: ' + destination);
throw e;
}
if (verbose) log.info('Test saved to ' + destination + ' successfully');
return true;
};
function uniformPath(path) {
var result = path.replaceAll('\\\\', '/');
return result;
}
module.exports.convertFiles = function (sourceDir, destinationDir, options) {
optionsTemplate = {
recursive: false,
verbose: true,
append: '_NUnit',
overwrite: false
};
options = options || optionsTemplate;
for (var key in optionsTemplate) {
if (!optionsTemplate.hasOwnProperty(key)) continue;
if (!options.hasOwnProperty(key)) {
options[key] = optionsTemplate[key];
}
}
if (!fs.existsSync(destinationDir)) {
throw new Error("NUnit destination doesn't exist. Please create the directory: " + destinationDir);
}
var recurPath = "";
if (options.recursive) recurPath = "/**";
// Get source file paths, taking into account whether or not to recur into subdirs
var sourcePaths = glob.sync(sourceDir + recurPath + "/*.cs");
for (var i = 0; i < sourcePaths.length; i++) {
// Resolve to absolute paths
sourcePaths[i] = path.resolve(sourcePaths[i]);
}
files = [];
for (var j = 0; j < sourcePaths.length; j++) {
var relativePath = sourcePaths[j].replace(path.resolve(sourceDir), '');
files.push({
sourcePath: uniformPath(sourcePaths[j]),
relativePath: uniformPath(relativePath),
destinationPath: uniformPath(path.resolve(destinationDir + relativePath)),
});
var dir = path.dirname(files[j].destinationPath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
module.exports.convertFile(files[j].sourcePath, files[j].destinationPath,
options.verbose, options.overwrite, options.append);
}
return true;
};