2011-03-24 4 views
5

나는 immutable class을 설계했는데, 그 이유는 값에 대한 의미론을 갖고 싶어하기 때문입니다. 나는 때때로 그 논평이 다른 팀 구성원에 의해 간과불변성 보장을위한 단위 테스트 만들기

// "This class is immutable, don't change this when adding new features to it."

는하지만 내가 알고있는 클래스의 해설 섹션에 힌트를 쓴, 그래서 추가 안전 장치로 단위 테스트를 작성하고 싶습니다. 이 아이디어를 어떻게 달성 할 수 있을까요? 리플렉션을 통해 클래스를 검사하여 생성자 만 내부 상태를 변경하는지 확인할 수 있습니까?

(누군가에게 중요하다면 C# 2.0 및 NUnit 사용).

+0

익명 다운 받던 사람에게 :이 질문에 잘못된 것이 있다고 생각되면 알려주십시오. –

답변

8

불변성을 테스트하기 위해 FieldInfo.IsInitOnly을 반복적으로 사용하는 방법에 대한 의견을 뒷받침하는 예제입니다.

내가 어떻게 string을 처리했는지 고려해야 할 특별한 경우가있을 수 있지만 잘못된 음수를 나타낼 수 있습니다. 즉, 뭔가 다른 것으로 바꾸어 놓을 수 있습니다.

로직은 모든 ​​필드가 readonly이어야하며 변경할 수없는 유형이어야합니다. 자체 참조 형식 또는 순환 참조에 대처할 수는 없습니다.

using System; 
using System.Linq; 
using System.Reflection; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 

namespace ImmutableTests 
{ 
    [TestClass] 
    public class AssertImmutableTests 
    { 
     [TestMethod] 
     public void Is_int_immutable() 
     { 
      Assert.IsTrue(Immutable<int>()); 
     } 

     [TestMethod] 
     public void Is_string_immutable() 
     { 
      Assert.IsTrue(Immutable<string>()); 
     } 

     [TestMethod] 
     public void Is_custom_immutable() 
     { 
      Assert.IsTrue(Immutable<MyImmutableClass>()); 
     } 

     [TestMethod] 
     public void Is_custom_mutable() 
     { 
      Assert.IsFalse(Immutable<MyMutableClass>()); 
     } 

     [TestMethod] 
     public void Is_custom_deep_mutable() 
     { 
      Assert.IsFalse(Immutable<MyDeepMutableClass>()); 
     } 

     [TestMethod] 
     public void Is_custom_deep_immutable() 
     { 
      Assert.IsTrue(Immutable<MyDeepImmutableClass>()); 
     } 

     [TestMethod] 
     public void Is_propertied_class_mutable() 
     { 
      Assert.IsFalse(Immutable<MyMutableClassWithProperty>()); 
     } 

     private static bool Immutable<T>() 
     { 
      return Immutable(typeof(T)); 
     } 

     private static bool Immutable(Type type) 
     { 
      if (type.IsPrimitive) return true; 
      if (type == typeof(string)) return true; 
      var fieldInfos = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); 
      var isShallowImmutable = fieldInfos.All(f => f.IsInitOnly); 
      if (!isShallowImmutable) return false; 
      var isDeepImmutable = fieldInfos.All(f => Immutable(f.FieldType)); 
      return isDeepImmutable; 
     } 
    } 

    public class MyMutableClass 
    { 
     private string _field; 
    } 

    public class MyImmutableClass 
    { 
     private readonly string _field; 
    } 

    public class MyDeepMutableClass 
    { 
     private readonly MyMutableClass _field; 
    } 

    public class MyDeepImmutableClass 
    { 
     private readonly MyImmutableClass _field; 
    } 

    public class MyMutableClassWithProperty 
    { 
     public string Prop { get; set; } 
    } 
} 
+0

좋은 접근. 감사합니다. – denver

+0

아마 내가 잘못된 것을보고 있지만 public int를 포함하는 클래스는이 예제에서 불변이라고 생각할 수 있습니다. 실제로는 변경 가능합니다. –

+0

@TonniTielens 당신이 무엇을 의미 하느냐에 따라,'public int A {get; 개인 집합;}'변경 가능,'public int A {get;}'변경할 수 없습니다. 'public int A;'변경 가능하다. – weston

3

이 게시물에는 몇 가지 아이디어가 있습니다. 그러나 확실한 방법이없는 것 같습니다.

How do I find out if a class is immutable in C#

+0

+1,이 대답은 유용합니다. 그러나 SO의 규칙에 따라 나는 하나만 받아 들일 수 있습니다. –

+1

"하나만있을 수 있습니다!"DO에서"outskeeted "하는 것은 드문 일이 아닙니다 : p – MattC

7

당신은 클래스가 봉인되어 있는지 확인하고, 각 필드는 읽기 전용되는 (FieldInfo.IsInitOnly 사용) 반사 검사를 사용 할 수있다.

물론 은 얕은입니다. 누군가가 그곳에 List<int> 필드를 넣은 다음 그 목록의 내용을 바꾸는 것을 멈추지 않을 것입니다.

+0

+1 그리고 받아 들였습니다. (95 %의 해결책으로 살 수 있습니다) –

+0

좋은 생각이 있는데, 이것을 사용하여 당신이 할 수있는'AssertImmutable ()'메소드를 만들었습니다. 필드 유형을 재귀 적으로 호출하여 깊은 불변성을 주장하십시오. – weston

+0

@weston 이것을 공유 했습니까? – denver

2

NDepend에 대해 들어 본 적이 없으면이 도구를 사용하면 소스 코드와 컴파일 된 어셈블리를 "검토"하고 종속성 검사 등을 비롯한 모든 종류의 마법을 수행 할 수 있습니다.

이러한 검사 중 하나는 불변의 검사입니다. 예를 들어, 나는 IImmutable 마커 인터페이스를 가지고 있고, 어떤 유형이 인터페이스를 가지고 있지만 변경할 경우 NDepend는 다음 쿼리를 사용하여 내 빌드를 실패

WARN IF Count > 0 IN SELECT TYPES WHERE 
    Implement "MyCompany.MyAssemblies.Dto.IImmutable" AND 
    !IsImmutable 

또한 위반 보고서를 생성하도록 구성뿐만 아니라 수 있습니다 실패한 빌드.

분명히 이것은 실제로 단위 테스트가 아닙니다. 그러나, 그것은 당신의 빌드의 일부로 통합 될 수 있고 단위 테스트처럼 당신의 빌드를 실패합니다. 그래서 나는 그것을 언급 할 것이라고 생각했습니다!

실제로 수행하는 작업과 방법에 대한 자세한 내용은 here을 참조하십시오.

+0

+1,이 대답은 유용하지만 역시 하나만 받아 들일 수 있습니다. 아마도 우리 프로젝트 가격 4에 합당한지를 알기 위해 NDepend를 시도 할 것입니다 .. –