복잡한 매개 변수를 [이론]에 전달
Xunit에는 멋진 기능 이 있습니다. Theory
속성으로 하나의 테스트를 만들고 속성에 데이터를 넣을 수 InlineData
있으며 xUnit은 많은 테스트를 생성하고 모두 테스트합니다.
나는 이런 식으로 뭔가를 갖고 싶어하지만, 내 방법에 매개 변수가없는 '단순 데이터'(등이있다 string
, int
, double
),하지만 내 클래스의 목록 :
public static void WriteReportsToMemoryStream(
IEnumerable<MyCustomClass> listReport,
MemoryStream ms,
StreamWriter writer) { ... }
xxxxData
XUnit 에는 많은 속성 이 있습니다 . 예를 들어 PropertyData
속성을 확인하십시오 .
를 반환하는 속성을 구현할 수 있습니다 IEnumerable<object[]>
. object[]
이 메서드가 생성하는 각 메서드는 메서드에 대한 단일 호출에 대한 매개 변수로 "압축 해제"됩니다 [Theory]
.
또 다른 옵션은 ClassData
동일하게 작동하지만 다른 클래스 / 네임 스페이스의 테스트간에 '생성자'를 쉽게 공유 할 수 있으며 실제 테스트 방법과 '데이터 생성기'를 분리합니다.
여기에서 다음 예를 참조 하십시오 .
PropertyData 예
public class StringTests2
{
[Theory, PropertyData(nameof(SplitCountData))]
public void SplitCount(string input, int expectedCount)
{
var actualCount = input.Split(' ').Count();
Assert.Equal(expectedCount, actualCount);
}
public static IEnumerable<object[]> SplitCountData
{
get
{
// Or this could read from a file. :)
return new[]
{
new object[] { "xUnit", 1 },
new object[] { "is fun", 2 },
new object[] { "to test with", 3 }
};
}
}
}
ClassData 예
public class StringTests3
{
[Theory, ClassData(typeof(IndexOfData))]
public void IndexOf(string input, char letter, int expected)
{
var actual = input.IndexOf(letter);
Assert.Equal(expected, actual);
}
}
public class IndexOfData : IEnumerable<object[]>
{
private readonly List<object[]> _data = new List<object[]>
{
new object[] { "hello world", 'w', 6 },
new object[] { "goodnight moon", 'w', -1 }
};
public IEnumerator<object[]> GetEnumerator()
{ return _data.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator()
{ return GetEnumerator(); }
}
@Quetzalcoatl의 대답을 업데이트하려면 다음을 수행하십시오. 속성 [PropertyData]
은 .NET Framework [MemberData]
를 반환하는 정적 메서드, 필드 또는 속성의 문자열 이름을 인수 로 사용 하는 것으로 대체되었습니다 IEnumerable<object[]>
. ( 한 번에 하나씩 테스트 사례를 실제로 계산할 수있는 반복기 메서드를 사용 하여 계산 된대로 산출 하는 것이 특히 좋습니다 .)
열거자가 반환하는 시퀀스의 각 요소는 an object[]
이고 각 배열은 길이가 같아야하며 해당 길이는 테스트 케이스에 대한 인수의 개수 여야합니다 (속성으로 주석 처리 [MemberData]
되고 각 요소는 해당 메서드 매개 변수와 동일한 유형을 가져야합니다). . (아니면 컨버터블 유형일 수도 있지만 모르겠습니다.)
( xUnit.net 2014 년 3 월 릴리스 노트 및 예제 코드가있는 실제 패치를 참조하십시오 .)
익명 객체 배열을 만드는 것은 데이터를 구성하는 가장 쉬운 방법이 아니므로이 패턴을 프로젝트에서 사용했습니다.
먼저 재사용 가능한 공유 클래스를 정의하십시오.
//http://stackoverflow.com/questions/22093843
public interface ITheoryDatum
{
object[] ToParameterArray();
}
public abstract class TheoryDatum : ITheoryDatum
{
public abstract object[] ToParameterArray();
public static ITheoryDatum Factory<TSystemUnderTest, TExpectedOutput>(TSystemUnderTest sut, TExpectedOutput expectedOutput, string description)
{
var datum= new TheoryDatum<TSystemUnderTest, TExpectedOutput>();
datum.SystemUnderTest = sut;
datum.Description = description;
datum.ExpectedOutput = expectedOutput;
return datum;
}
}
public class TheoryDatum<TSystemUnderTest, TExecptedOutput> : TheoryDatum
{
public TSystemUnderTest SystemUnderTest { get; set; }
public string Description { get; set; }
public TExpectedOutput ExpectedOutput { get; set; }
public override object[] ToParameterArray()
{
var output = new object[3];
output[0] = SystemUnderTest;
output[1] = ExpectedOutput;
output[2] = Description;
return output;
}
}
이제 개별 테스트 및 멤버 데이터를 더 쉽게 작성하고 정리할 수 있습니다.
public class IngredientTests : TestBase
{
[Theory]
[MemberData(nameof(IsValidData))]
public void IsValid(Ingredient ingredient, string testDescription, bool expectedResult)
{
Assert.True(ingredient.IsValid == expectedResult, testDescription);
}
public static IEnumerable<object[]> IsValidData
{
get
{
var food = new Food();
var quantity = new Quantity();
var data= new List<ITheoryDatum>();
data.Add(TheoryDatum.Factory(new Ingredient { Food = food } , false, "Quantity missing"));
data.Add(TheoryDatum.Factory(new Ingredient { Quantity = quantity } , false, "Food missing"));
data.Add(TheoryDatum.Factory(new Ingredient { Quantity = quantity, Food = food } , true, "Valid"));
return data.ConvertAll(d => d.ToParameterArray());
}
}
}
문자열 Description
속성은 많은 테스트 사례 중 하나가 실패 할 때 뼈를 던지는 것입니다.
이 방법으로 시도 할 수 있습니다.
public class TestClass {
bool isSaturday(DateTime dt)
{
string day = dt.DayOfWeek.ToString();
return (day == "Saturday");
}
[Theory]
[MemberData("IsSaturdayIndex", MemberType = typeof(TestCase))]
public void test(int i)
{
// parse test case
var input = TestCase.IsSaturdayTestCase[i];
DateTime dt = (DateTime)input[0];
bool expected = (bool)input[1];
// test
bool result = isSaturday(dt);
result.Should().Be(expected);
}
}
테스트 데이터를 보관할 다른 클래스를 만듭니다.
public class TestCase
{
public static readonly List<object[]> IsSaturdayTestCase = new List<object[]>
{
new object[]{new DateTime(2016,1,23),true},
new object[]{new DateTime(2016,1,24),false}
};
public static IEnumerable<object[]> IsSaturdayIndex
{
get
{
List<object[]> tmp = new List<object[]>();
for (int i = 0; i < IsSaturdayTestCase.Count; i++)
tmp.Add(new object[] { i });
return tmp;
}
}
}
Manufacturer 클래스가있는 복잡한 Car 클래스가 있다고 가정합니다.
public class Car
{
public int Id { get; set; }
public long Price { get; set; }
public Manufacturer Manufacturer { get; set; }
}
public class Manufacturer
{
public string Name { get; set; }
public string Country { get; set; }
}
우리는 Car 클래스를 채우고 이론 테스트를 통과 할 것입니다.
따라서 아래와 같이 Car 클래스의 인스턴스를 반환하는 'CarClassData'클래스를 만듭니다.
public class CarClassData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] {
new Car
{
Id=1,
Price=36000000,
Manufacturer = new Manufacturer
{
Country="country",
Name="name"
}
}
};
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
테스트 메소드 (CarTest)를 생성하고 자동차를 매개 변수로 정의 할 때입니다.
[Theory]
[ClassData(typeof(CarClassData))]
public void CarTest(Car car)
{
var output = car;
var result = _myRepository.BuyCar(car);
}
행운을 빕니다
내 필요에 따라 몇 가지 테스트를 통해 일련의 '테스트 사용자'를 실행하고 싶었지만 [ClassData] 등은 내가 필요한 항목에 대해 과도하게 보였습니다 (항목 목록이 각 테스트에 현지화 되었기 때문).
그래서 테스트 내부에 배열을 사용하여 다음을 수행했습니다.
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
public async Task Account_ExistingUser_CorrectPassword(int userIndex)
{
// DIFFERENT INPUT DATA (static fake users on class)
var user = new[]
{
EXISTING_USER_NO_MAPPING,
EXISTING_USER_MAPPING_TO_DIFFERENT_EXISTING_USER,
EXISTING_USER_MAPPING_TO_SAME_USER,
NEW_USER
} [userIndex];
var response = await Analyze(new CreateOrLoginMsgIn
{
Username = user.Username,
Password = user.Password
});
// expected result (using ExpectedObjects)
new CreateOrLoginResult
{
AccessGrantedTo = user.Username
}.ToExpectedObject().ShouldEqual(response);
}
이것은 테스트의 의도를 명확하게 유지하면서 내 목표를 달성했습니다. 인덱스를 동기화 상태로 유지하기 만하면되지만 그게 전부입니다.
Looks nice in the results, it's collapsable and you can rerun a specific instance if you get an error:
I guess you mistaken here. What xUnit Theory
attribute actually means: You want to test this function by sending special/random values as parameters that this function-under-test receives. That means that what you define as the next attribute, such as: InlineData
, PropertyData
, ClassData
, etc.. will be the source for those parameters. That means that you should construct the source object to provide those parameters. In your case I guess you should use ClassData
object as source. Also - please note that ClassData
inherits from: IEnumerable<>
- that means each time another set of generated parameters will be used as incoming parameters for function-under-test until IEnumerable<>
produces values.
Example here: Tom DuPont .NET
Example may be incorrect - I didn't use xUnit for a long time
참고URL : https://stackoverflow.com/questions/22093843/pass-complex-parameters-to-theory
'Programing' 카테고리의 다른 글
C ++ 11에서 열거 형 클래스의 값을 어떻게 출력 할 수 있습니까? (0) | 2020.10.05 |
---|---|
스타일 / 템플릿을 사용하여 wpf에서 소수점 이하 자릿수를 포맷하는 방법은 무엇입니까? (0) | 2020.10.05 |
Volley를 사용하여 JSON 데이터로 POST 요청 보내기 (0) | 2020.10.05 |
개인 도커 레지스트리에 원격 액세스하는 방법은 무엇입니까? (0) | 2020.10.05 |
C 및 C ++에서 (int) + 4 * 5 표현식을 허용하는 이유는 무엇입니까? (0) | 2020.10.05 |