날씨 파싱 메서드를 단위 테스트하고 싶습니다. 내 첫 번째 접근 방식은 autofixture가 weather 객체를 만들고 그 객체로부터 쿼리 응답을 만드는 것입니다. 그러나 날씨 클래스는 여러 제한 사항이 포함 :규칙을 사용하여 객체 만들기
- 습도는 백분율 값이며, 온도는 온도 단위
에 따라 최소 이상이어야
날씨 파싱 메서드를 단위 테스트하고 싶습니다. 내 첫 번째 접근 방식은 autofixture가 weather 객체를 만들고 그 객체로부터 쿼리 응답을 만드는 것입니다. 그러나 날씨 클래스는 여러 제한 사항이 포함 :규칙을 사용하여 객체 만들기
에 따라 최소 이상이어야
outlined elsewhere으로 테스트 구동 개발을 통해 설계에 대한 피드백을 제공 할 수있는 솔루션을 권장합니다. 습도와 온도를 원시로 처리하는 대신 refactor to a good domain model. 예를 들어, 모두에 대해 새 값 객체를 생성 :
public struct Humidity
{
public readonly byte percentage;
public Humidity(byte percentage)
{
if (100 < percentage)
throw new ArgumentOutOfRangeException(
nameof(percentage),
"Percentage must be a number between 0 and 100.");
this.percentage = percentage;
}
public static explicit operator byte(Humidity h)
{
return h.percentage;
}
public static explicit operator int(Humidity h)
{
return h.percentage;
}
public override bool Equals(object obj)
{
if (obj is Humidity)
return ((Humidity)obj).percentage == this.percentage;
return base.Equals(obj);
}
public override int GetHashCode()
{
return this.percentage.GetHashCode();
}
}
유형 Celcius
은 비슷합니다
public struct Celcius
{
private readonly decimal degrees;
public Celcius(decimal degrees)
{
if (degrees < -273.15m)
throw new ArgumentOutOfRangeException(
nameof(degrees),
"Degrees Celsius must be equal to, or above, absolute zero.");
this.degrees = degrees;
}
public static explicit operator decimal(Celcius c)
{
return c.degrees;
}
public override bool Equals(object obj)
{
if (obj is Celcius)
return ((Celcius)obj).degrees == this.degrees;
return base.Equals(obj);
}
public override int GetHashCode()
{
return this.degrees.GetHashCode();
}
}
이렇게하면 Humidity
또는 Celcius
객체가있는 경우가 있기 때문에, 유효 걸 보장 그들은 불변성을 보호합니다. 이는 프로덕션 코드에서 유용하지만 테스트 이점도 제공합니다. 만약 당신이 좋아하면
public class Weather
{
public Humidity Humidity { get; }
public Celcius Temperature { get; }
public Weather(Humidity humidity, Celcius temperature)
{
this.Humidity = humidity;
this.Temperature = temperature;
}
}
당신은뿐만 아니라 Weather
에 대한 Equals
및 GetHashCode
을 대체 할 수 있지만,이 예를 들어 중요하지 :
Weather
은 단순히 지금, 다음과 같습니다.
AutoFixture를 들어, 당신은 지금 두 가지 유형에 대한 정의를 정의 할 수 있습니다 :
public class HumidityCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(new HumidityBuilder());
}
private class HumidityBuilder : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
var t = request as Type;
if (t == null || t != typeof(Humidity))
return new NoSpecimen();
var d =
context.Resolve(
new RangedNumberRequest(
typeof(byte),
byte.MinValue,
(byte)100));
return new Humidity((byte)d);
}
}
}
및
이public class CelciusCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(new CelciusBuilder());
}
private class CelciusBuilder : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
var t = request as Type;
if (t == null || t != typeof(Celcius))
return new NoSpecimen();
var d =
context.Resolve(
new RangedNumberRequest(
typeof(decimal),
-273.15m,
decimal.MaxValue));
return new Celcius((decimal)d);
}
}
}
당신은 CompositeCustomization
에서 그 (등)를 수집 할 수 있습니다
public class MyConventions : CompositeCustomization
{
public MyConventions() : base(
new CelciusCustomization(),
new HumidityCustomization())
{
}
}
이제 다음과 같은 간단한 테스트를 작성할 수 있습니다 :
[Fact]
public void FixtureCanCreateValidWeather()
{
var fixture = new Fixture().Customize(new MyConventions());
var actual = fixture.Create<Weather>();
Assert.True((int)actual.Humidity <= 100);
Assert.True(-273.15m <= (decimal)actual.Temperature);
}
이 테스트에 합격했습니다.
이 테스트는 단일 테스트의 많은 작업과 비슷하지만 모든 도메인 별 사용자 지정 내용을 MyConventions
에 수집하는 경우 수백 개의 테스트에 걸쳐 해당 단일 규칙을 다시 사용할 수 있습니다. 도메인 개체가 유효합니다.
테스트 코드를보다 관리하기 쉬울뿐만 아니라 프로덕션 코드를보다 관리하기 쉽게 만듭니다.
이것은 훌륭한 대답입니다. 그러나 유효한 도메인 프리미티브를 만드는 데는 상당한 시간이 걸릴 것으로 보입니다 (귀하가 가지고 있다고 가정하거나 그러한 불변 유지 유형으로 리팩터링 할 수 있음). AutoFixture가 이해할 수있는 선언적 접근법은 이상적이지만 속성을 사용하여 주석을 추가하는 것은 잘못되었습니다. 세 번째 접근 방식이 궁금하거나 C#의 표현의 한계를 충족 시키는가? – Schneider
@Schneider 세 번째 접근법이있는 경우이를 인식하지 못합니다. 나는 여러 해 동안 수색을하고 마침내 F #에 찬성하여 C#을 포기했다. F #에서는 선언적이며 간결한 방식으로 이러한 유형을 정의 할 수 있습니다. –
단위 테스트가 날씨 개체의 테스트 * * 인 경우, 난 그냥 하드 코드 것입니다. 하지만 많은 것들과 마찬가지로, 그것은 다릅니다 ... – mxmissile
비슷한 질문에 대한 답변입니다 : http://stackoverflow.com/a/22333452/126014 –
@mxmissile 예, 파서는 날씨 객체의 소비자입니다. – R3turnz