奇数的 Xunit 测试
Xunit Test for odd numbers
我正在努力提高我的单元测试技能,我想知道是否有人可以告诉我编写测试“给定整数数组的排序奇数”测试的最佳方法。目前我正在使用参数将数据传递到测试中,但我也会验证输出是否正确。
[Theory]
[InlineData(1,2,3,4)]
public void SortArrayOddNumber(params int[] number)
{
var s = number.Where(n => (n % 2) == 0).OrderBy(n=>n);
}
您需要将预期输出作为参数传递。从最基本的角度来看,单元测试应该是这样的:
- 输入数据,最好是测试方法需要的所有参数,
- 单元测试方法的名称应该是描述性的,并准确说明它在测试什么,
- 应该将测试方法的结果 return 与预期值进行比较。
示例:
项目中的一些 class - 假设 MyLib 库项目:
Sort.cs
using System.Linq;
namespace MyLib
{
public class Sort
{
public int[] SortArrayOddNumber(int[] arrayToSort)
{
return arrayToSort
.Where(n => (n % 2) == 0)
.OrderBy(n => n)
.ToArray();
}
}
}
测试项目MyLib.Tests:
SortTest.cs
using Xunit;
namespace MyLib.Tests
{
public class SortTest
{
// System under test
private readonly Sort sut;
public SortTest()
{
sut = new Sort();
}
[Theory]
[InlineData(new[] { 1, 2, 3, 4}, new[] { 2, 4 })]
public void SortArrayOddNumber_should_return_array_with_only_even_numbers_sorded_inc(
int[] arrayToSort,
int[] expectedResult)
{
// Arrange
// well... nothing to do before running our method we are testing...
// Act
var result = sut.SortArrayOddNumber(arrayToSort);
// Assert
Assert.Equal(expected: expectedResult, actual: result);
}
}
}
所以:
- 我们有方法要测试,
- 我们为每个要测试的项目创建 *.Tests 项目 (A - A.Tests),
- 我们为项目中的每个 class 创建一个 class 在测试项目中 (Sort.cs - SortTest.cs)
- 我们添加对项目的引用
- 我们在构造函数中创建一个 class 我们将进行测试,
- 我们定义了我们想要的代码的所有要求,我们只是以方法的形式编写它们,
- 方法是 运行 检查我们方法的给定 属性 的代码。
最后的几点评论:
- 测试方法的命名方式有多种。总的来说,我花了很多时间试图挑选最好的一个,但我会说它应该只满足 1 个条件:
reading the name should get You an answer what does this method check
。也许第二个:stick to the selected convention
- 您应该从不计算预期结果。您在那里或在您正在测试的代码中犯错误的可能性是一样的。您应该事先知道输入是什么以及您想从该方法中获得什么。所以你不应该通过重新计算来检查结果,而是以一种不同的、“更安全”的方式。
您应该将输入数组和预期数组作为测试中的参数传递。
您可以像这样使用 InlineData :
[Theory]
[InlineData(new[] { 0, 1, 2, 3, 4 }, new[] { 0, 2, 4 })]
[InlineData(new[] { 0, 1, 3, 4 }, new[] { 0, 4 })]
public void Should_return_odd_numbers_in_ascending_order(int[] input, int[] expectedSortedArray)
{
var result = input.SortOddNumbers();
result.Should().BeEquivalentTo(expectedSortedArray, opt => opt.WithStrictOrdering());
}
或者您可以像这样使用 MemberData :
public static IEnumerable<object[]> Data => new List<object[]>
{
new object[] {new[] {0, 1, 2, 3, 4}, new[] {0, 2, 4}},
new object[] {new[] {0, 1, 3, 4}, new[] {0, 4}}
};
[Theory, MemberData(nameof(Data))]
public void Should_return_odd_numbers_in_ascending_order(int[] input, int[] expectedSortedArray)
{
var result = input.SortOddNumbers();
result.Should().BeEquivalentTo(expectedSortedArray, opt => opt.WithStrictOrdering());
}
和 SortOddNumbers 扩展方法:
public static class NumberExtensions
{
public static int[] SortOddNumbers(this int[] input)
{
return input.Where(i => (i % 2) == 0).OrderBy(i => i).ToArray();
}
}
首先,您正在编写测试内部的逻辑。
您想测试一个单独的 class。
其次,你会把预期的结果传到哪里?
你可以找到一种方法将多个数据传递给你的测试,就像 Arsalan 在他的回答中说的那样,或者你的断言应该检查所有数字都是奇数并且它们是按升序排列的。
你可以查看 FluentAssertions
库,然后像这样编写你的测试:
// The class/method you are testing
public class MyLogic
{
public int[] SortOddNumbers(int[] input)
{
return input.Where(n => (n%2) == 1).OrderBy(n => n).ToArray();
}
}
测试:
public class MyLogic.Tests
{
[Theory]
[InlineData(6,1,2,3,10,9,8)]
public void SortOddNumbers_ShouldReturn_OddNumbersInAscendingOrder(params int[] input)
{
// Arrange
var logic = new MyLogic();
// Act
var actual = logic.SortOddNumbers(input);
// Assert
actual.Should().OnlyContain(x => x % 2 == 1);
actual.Should().BeInAscendingOrder(x => x);
}
}
编辑:要找到有关如何将数据传递给测试的多种方法(和灵感),这里有一篇 Andrew Lock 的优秀文章:https://andrewlock.net/creating-parameterised-tests-in-xunit-with-inlinedata-classdata-and-memberdata/
你并不是真的在写单元测试。您正在测试中编写一些代码并希望对其进行验证。通常,您会在其他地方尝试验证一种方法。假设您这样做:
public static Numbers
{
public static IEnumerable<int> FilterAndSortOdds(IEnumerable<int> source)
=> source.Where(i => (i%2) != 0).OrderBy(i => i);
}
然后您将验证从您的测试中调用该方法。例如,一个简单的 Fact
可能如下所示:
public class TestClass
{
[Fact]
public void CanSortAndFilterOddNumbers()
{
var source = new[] { 1, 2, 9, 4, 5 };
var expected = new[] { 1, 5, 9 };
var actual = Numbers.FilterAndSortOdds(source).ToArray();
Assert.Equal(expected, actual);
}
}
现在如果你想把它变成一个理论,你有几个选择。您可以使用 InlineData
(通过创建新数组)
public class TestClass
{
[Theory]
[InlineData(new[] { 1, 2, 9, 4, 5 }, new[] { 1, 5, 9 })]
[InlineData(new[] { 2, 1 }, new[] { 1 })]
[InlineData(new[] { 9, 7, 5, 3, 1 }, new[] { 1, 3, 5, 7, 9 })]
public void CanSortAndFilterOddNumbers(int[] source, int[] expected)
{
var actual = Numbers.FilterAndSortOdds(source).ToArray();
Assert.Equal(expected, actual);
}
您也可以使用 MemberData
:
public class TestClass
{
[Theory]
[MemberData(nameof(PassingData)]
public void CanSortAndFilterOddNumbers(int[] source, int[] expected)
{
var actual = Numbers.FilterAndSortOdds(source).ToArray();
Assert.Equal(expected, actual);
}
public static IEnumerable<object[]> PassingData => new List<object[]> {
/* Test 1 */
new object[] { new[] { 1, 2, 9, 4, 5 }, new[] { 1, 5, 9 } },
/* Test 2 */
new object[] { new[] { 2, 1 }, new[] { 1 } },
/* Test 3 */
new object[] { new[] { 9, 7, 5, 3, 1 }, new[] { 1, 3, 5, 7, 9 } }
};
}
对于更强类型的东西,我们可以使用 ClassData
和 TheoryData<>
助手:
public class TestClass
{
[Theory]
[ClassData(nameof(PassingOdds)]
public void CanSortAndFilterOddNumbers(IEnumerable<int> source, int[] expected)
{
var actual = Numbers.FilterAndSortOdds(source).ToArray();
Assert.Equal(expected, actual);
}
}
public class PassingOdds :
TheoryData</*arg1*/IEnumerable<int>, /*arg2*/int[]>
{
public PassingOdds()
{
/* Test 1*/
Add(new[] { 1, 2, 9, 4, 5 }, new[] { 1, 5, 9 });
/* Test 2 */
Add(new List<int> { 2, 1 }, new[] { 1 });
/* Test 3 */
Add(new[] { 9, 7, 5, 3, 1 }, new[] { 1, 3, 5, 7, 9 });
}
}
ClassData
也可以用于类似于 MemberData
的非通用(基于 IEnumerable<object[]>
的)模型。 Andrew Lock 有一对很棒的 blog posts on the topic. You can also see this in the xUnit samples on github .
我正在努力提高我的单元测试技能,我想知道是否有人可以告诉我编写测试“给定整数数组的排序奇数”测试的最佳方法。目前我正在使用参数将数据传递到测试中,但我也会验证输出是否正确。
[Theory]
[InlineData(1,2,3,4)]
public void SortArrayOddNumber(params int[] number)
{
var s = number.Where(n => (n % 2) == 0).OrderBy(n=>n);
}
您需要将预期输出作为参数传递。从最基本的角度来看,单元测试应该是这样的:
- 输入数据,最好是测试方法需要的所有参数,
- 单元测试方法的名称应该是描述性的,并准确说明它在测试什么,
- 应该将测试方法的结果 return 与预期值进行比较。
示例:
项目中的一些 class - 假设 MyLib 库项目:
Sort.cs
using System.Linq;
namespace MyLib
{
public class Sort
{
public int[] SortArrayOddNumber(int[] arrayToSort)
{
return arrayToSort
.Where(n => (n % 2) == 0)
.OrderBy(n => n)
.ToArray();
}
}
}
测试项目MyLib.Tests:
SortTest.cs
using Xunit;
namespace MyLib.Tests
{
public class SortTest
{
// System under test
private readonly Sort sut;
public SortTest()
{
sut = new Sort();
}
[Theory]
[InlineData(new[] { 1, 2, 3, 4}, new[] { 2, 4 })]
public void SortArrayOddNumber_should_return_array_with_only_even_numbers_sorded_inc(
int[] arrayToSort,
int[] expectedResult)
{
// Arrange
// well... nothing to do before running our method we are testing...
// Act
var result = sut.SortArrayOddNumber(arrayToSort);
// Assert
Assert.Equal(expected: expectedResult, actual: result);
}
}
}
所以:
- 我们有方法要测试,
- 我们为每个要测试的项目创建 *.Tests 项目 (A - A.Tests),
- 我们为项目中的每个 class 创建一个 class 在测试项目中 (Sort.cs - SortTest.cs)
- 我们添加对项目的引用
- 我们在构造函数中创建一个 class 我们将进行测试,
- 我们定义了我们想要的代码的所有要求,我们只是以方法的形式编写它们,
- 方法是 运行 检查我们方法的给定 属性 的代码。
最后的几点评论:
- 测试方法的命名方式有多种。总的来说,我花了很多时间试图挑选最好的一个,但我会说它应该只满足 1 个条件:
reading the name should get You an answer what does this method check
。也许第二个:stick to the selected convention
- 您应该从不计算预期结果。您在那里或在您正在测试的代码中犯错误的可能性是一样的。您应该事先知道输入是什么以及您想从该方法中获得什么。所以你不应该通过重新计算来检查结果,而是以一种不同的、“更安全”的方式。
您应该将输入数组和预期数组作为测试中的参数传递。
您可以像这样使用 InlineData :
[Theory]
[InlineData(new[] { 0, 1, 2, 3, 4 }, new[] { 0, 2, 4 })]
[InlineData(new[] { 0, 1, 3, 4 }, new[] { 0, 4 })]
public void Should_return_odd_numbers_in_ascending_order(int[] input, int[] expectedSortedArray)
{
var result = input.SortOddNumbers();
result.Should().BeEquivalentTo(expectedSortedArray, opt => opt.WithStrictOrdering());
}
或者您可以像这样使用 MemberData :
public static IEnumerable<object[]> Data => new List<object[]>
{
new object[] {new[] {0, 1, 2, 3, 4}, new[] {0, 2, 4}},
new object[] {new[] {0, 1, 3, 4}, new[] {0, 4}}
};
[Theory, MemberData(nameof(Data))]
public void Should_return_odd_numbers_in_ascending_order(int[] input, int[] expectedSortedArray)
{
var result = input.SortOddNumbers();
result.Should().BeEquivalentTo(expectedSortedArray, opt => opt.WithStrictOrdering());
}
和 SortOddNumbers 扩展方法:
public static class NumberExtensions
{
public static int[] SortOddNumbers(this int[] input)
{
return input.Where(i => (i % 2) == 0).OrderBy(i => i).ToArray();
}
}
首先,您正在编写测试内部的逻辑。
您想测试一个单独的 class。
其次,你会把预期的结果传到哪里?
你可以找到一种方法将多个数据传递给你的测试,就像 Arsalan 在他的回答中说的那样,或者你的断言应该检查所有数字都是奇数并且它们是按升序排列的。
你可以查看 FluentAssertions
库,然后像这样编写你的测试:
// The class/method you are testing
public class MyLogic
{
public int[] SortOddNumbers(int[] input)
{
return input.Where(n => (n%2) == 1).OrderBy(n => n).ToArray();
}
}
测试:
public class MyLogic.Tests
{
[Theory]
[InlineData(6,1,2,3,10,9,8)]
public void SortOddNumbers_ShouldReturn_OddNumbersInAscendingOrder(params int[] input)
{
// Arrange
var logic = new MyLogic();
// Act
var actual = logic.SortOddNumbers(input);
// Assert
actual.Should().OnlyContain(x => x % 2 == 1);
actual.Should().BeInAscendingOrder(x => x);
}
}
编辑:要找到有关如何将数据传递给测试的多种方法(和灵感),这里有一篇 Andrew Lock 的优秀文章:https://andrewlock.net/creating-parameterised-tests-in-xunit-with-inlinedata-classdata-and-memberdata/
你并不是真的在写单元测试。您正在测试中编写一些代码并希望对其进行验证。通常,您会在其他地方尝试验证一种方法。假设您这样做:
public static Numbers
{
public static IEnumerable<int> FilterAndSortOdds(IEnumerable<int> source)
=> source.Where(i => (i%2) != 0).OrderBy(i => i);
}
然后您将验证从您的测试中调用该方法。例如,一个简单的 Fact
可能如下所示:
public class TestClass
{
[Fact]
public void CanSortAndFilterOddNumbers()
{
var source = new[] { 1, 2, 9, 4, 5 };
var expected = new[] { 1, 5, 9 };
var actual = Numbers.FilterAndSortOdds(source).ToArray();
Assert.Equal(expected, actual);
}
}
现在如果你想把它变成一个理论,你有几个选择。您可以使用 InlineData
(通过创建新数组)
public class TestClass
{
[Theory]
[InlineData(new[] { 1, 2, 9, 4, 5 }, new[] { 1, 5, 9 })]
[InlineData(new[] { 2, 1 }, new[] { 1 })]
[InlineData(new[] { 9, 7, 5, 3, 1 }, new[] { 1, 3, 5, 7, 9 })]
public void CanSortAndFilterOddNumbers(int[] source, int[] expected)
{
var actual = Numbers.FilterAndSortOdds(source).ToArray();
Assert.Equal(expected, actual);
}
您也可以使用 MemberData
:
public class TestClass
{
[Theory]
[MemberData(nameof(PassingData)]
public void CanSortAndFilterOddNumbers(int[] source, int[] expected)
{
var actual = Numbers.FilterAndSortOdds(source).ToArray();
Assert.Equal(expected, actual);
}
public static IEnumerable<object[]> PassingData => new List<object[]> {
/* Test 1 */
new object[] { new[] { 1, 2, 9, 4, 5 }, new[] { 1, 5, 9 } },
/* Test 2 */
new object[] { new[] { 2, 1 }, new[] { 1 } },
/* Test 3 */
new object[] { new[] { 9, 7, 5, 3, 1 }, new[] { 1, 3, 5, 7, 9 } }
};
}
对于更强类型的东西,我们可以使用 ClassData
和 TheoryData<>
助手:
public class TestClass
{
[Theory]
[ClassData(nameof(PassingOdds)]
public void CanSortAndFilterOddNumbers(IEnumerable<int> source, int[] expected)
{
var actual = Numbers.FilterAndSortOdds(source).ToArray();
Assert.Equal(expected, actual);
}
}
public class PassingOdds :
TheoryData</*arg1*/IEnumerable<int>, /*arg2*/int[]>
{
public PassingOdds()
{
/* Test 1*/
Add(new[] { 1, 2, 9, 4, 5 }, new[] { 1, 5, 9 });
/* Test 2 */
Add(new List<int> { 2, 1 }, new[] { 1 });
/* Test 3 */
Add(new[] { 9, 7, 5, 3, 1 }, new[] { 1, 3, 5, 7, 9 });
}
}
ClassData
也可以用于类似于 MemberData
的非通用(基于 IEnumerable<object[]>
的)模型。 Andrew Lock 有一对很棒的 blog posts on the topic. You can also see this in the xUnit samples on github .