오픈소스 에디터 프로그램으로 유명한 'Notepad++'에서 C# 언어를 스크립트 언어로 사용할 수 있게 해준다.


 'CS-Script'라는 C#의 오픈소스 스크립트 라이브러리를 Notepad++에서 플러그인으로 붙인 것이다.


인텔리센스와 디버깅까지 지원해준다.


닷넷의 기본 기능(Winform 지원)Notepad++의  기능을 확장할 수 있다.


프로젝트 사이트: http://csscriptnpp.codeplex.com/

코드 프로젝트의 설명: http://www.codeproject.com/Articles/694248/Sharpening-Notepadplusplus 



CS-Script for Notepad++ 설치



CS-Script for Notepad++ 사용

'Project Panel' 버튼을 클릭하면 위 화면 처럼 오른쪽에 패널이 생긴다. 여기에서 스크립트 파일을 추가하고 실행한다.







저작자 표시
신고
by 흥배 2014.02.05 08:00


Log4netTest.zip

log4net 이란

아파치의 오픈소스 로그 라이브러리로 Java, C++, .NET 다양한 언어와 플랫폼을 지원하는 로그 라이브러리의 .NET 버전이다. 로그를 파일, 콘솔화면, DB 출력할 있고 날짜, 파일 크기마다 로그를 새로 생성하도록 있다.

 

.NET에서 사용하기

  • .NET 경우 NuGet 사용하여 설치한다.

 

  • NuGet 사용하여 log4net 라이브러리를 설치하면 아래 그림처럼 자동으로 프로젝트에 설정된다.

 

  • 상태에서 빌드를 한다. 실행 파일이 있는 위치에 log4net 관련 dll xml 파일이 있다.

 

  • NuGet으로 설치한 경우 log4net 관련 설정 정보는 app.config 기록한다.

 

  • 로그가 남도록 하면 아래와 같이 로그 파일이 생겨진다.

 

  • 프로그램 실행 도중 로그 설정 파일을 변경하여 적용하기 위해서는 아래 처럼 AssemblyInfo.cs [assembly: log4net.Config.XmlConfigurator(Watch = true)] 추가한다.

 

  • 로그 설정 정보가 있는 app.confog

 

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

    <startup>

        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />

    </startup>

   

    <log4net>

      <!-- 파일을 사용하는 Appender -->

      <appender name="LogToFile" type="log4net.Appender.FileAppender" >

   

        <!-- 만들 로그 파일 -->

        <param name="File" value="log-file.txt" />

        <!-- 기존에 로그 파일이 있다면 연결해서 사용 여부 -->

        <param name="AppendToFile" value="true" />

        <!-- 로그 형식 -->

        <layout type="log4net.Layout.PatternLayout">

          <param name="ConversionPattern" value="%date [%thread] %-5level %logger - %message%newline" />

        </layout>

      </appender>

   

      <!-- 날짜 단위로 Rolling 하는 파일 Appender -->

      <appender name="DayRollingLogToFile" type="log4net.Appender.RollingFileAppender">

        <!-- 로그 파일 이름의 선두 부분 -->

        <File value="LogSample" />

        <!-- 기존에 로그 파일이 있다면 연결해서 사용 여부 -->

        <appendToFile value="true" />

        <!-- 날짜 마다 파일을 작성하는 것을 지정 -->

        <rollingStyle value="date" />

        <!-- 로그 파일 이름 고정 여부. 고정하지 않을 때는 false -->

        <staticLogFileName value="false" />

        <!-- 파일 이름에의 날짜 부분 -->

        <datePattern value='"."yyyyMMdd".log"' />

   

        <layout type="log4net.Layout.PatternLayout">

          <!-- 로그 형식 -->

          <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />

        </layout>

      </appender>

   

      <!-- 파일 크기 단위로 Rolling 하는 파일 Appender -->

      <appender name="SizeRollingLogToFile" type="log4net.Appender.RollingFileAppender">

        <file value="mylogfile.txt" />

        <appendToFile value="true" />

        <rollingStyle value="Size" />

        <maxSizeRollBackups value="5" />

        <maximumFileSize value="10MB" />

        <staticLogFileName value="true" />

        <layout type="log4net.Layout.PatternLayout">

          <conversionPattern value="%date [%thread] %level %logger - %message%newline" />

        </layout>

      </appender>

   

      <!-- 콘솔을 사용하는 Appender -->

      <appender name="LogToConsole" type="log4net.Appender.ConsoleAppender">

        <layout type="log4net.Layout.PatternLayout">

          <conversionPattern value="%date{ABSOLUTE} [%thread] %level %logger - %message%newline"/>

        </layout>

      </appender>

   

      <root>

        <!-- 로그 레벨 지정. 아래에서는 INFO 레벨 이상만 로그가 남는다 -->

        <level value="INFO" />

        <!-- 아래 로그 레벨 중 하나 선택

        <level value="OFF " />

        <level value="FATAL" />

        <level value="ERROR" />

        <level value="WARN" />

        <level value="INFO" />

        <level value="DEBUG" />

        <level value="ALL" />

        -->

        <!-- 어느 로그를 사용할 것인지 지정한다. 현재 파일로그와 날짜별 로그 사용 -->

        <appender-ref ref="LogToFile" />

        <appender-ref ref="DayRollingLogToFile" />

        <!-- 복수 지정 가능 -->

      </root>

    </log4net>

     

</configuration>

 

  • 예제 소스 코드

 

public partial class Form1 : Form

{

   private static readonly log4net.ILog log =

   log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

   

   public Form1()

   {

       InitializeComponent();

   }

   

   private void button1_Click(object sender, EventArgs e)

   {

       log.Info("INFO 출력");

       log.Debug("Debug 출력");

       log.Error("Error 출력");

   }

}

  

 

로그 정보에 로그를 남기는 코드 부분의 파일명이나 위치를 남기고 싶은 경우

로그 형식에 %location 사용한다.

 

<param name="ConversionPattern" value="%date [%thread] %-5level %logger - %location %message%newline" />

  • 로그 파일을 특정 폴더에 남기고 싶은경우

  • 현재 실행 파일이 있는 폴더의 Logs 폴더에 남기고 싶은 경우

 

<log4net>

    <!--날짜 단위로 Rolling 하는 파일 Appender-->

    <appender name="DayRollingLogToFile" type="log4net.Appender.RollingFileAppender">

      <!--로그 파일 이름의 선두 부분-->

      <File value="Logs\GameLog" />

 

로그 설정 정보를 app.config 파일 이외의 파일에 남기고 싶을

'AssemblyInfo.cs' 파일에 아래의 코드를 입력한다

 

[assembly: log4net.Config.DOMConfigurator(ConfigFile = "../../log4net.xml", Watch = true)]

또는

 

// 로그 설정 파일 로딩 후 적용

string logFilePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log4net_config.xml");

var finfo = new System.IO.FileInfo(logFilePath);

log4net.Config.XmlConfigurator.ConfigureAndWatch(finfo);

logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

 

nunit에서 사용할 동작하지 않는 경우

Log4Net.config 파일을 만들어서 아래와 같이 만든다.

 

<?xml version="1.0" encoding="utf-8" ?>

<log4net>

<!-- 필요한 설정 정보 입력 -->

</log4net>

소스에서 위에서 만든 설정 파일을 사용하도록 한다.

 

[SetUp]

public void RunBeforeAnyTests()

{

    string logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Log4Net.config");

    FileInfo finfo = new FileInfo(logFilePath);

    log4net.Config.XmlConfigurator.ConfigureAndWatch(finfo);

}

 

로그를 메일로 보내기

  • 로그 설정 파일에서 log4net.Appender.SmtpAppender 사용한다.

  • 보낼 메일의 문지 인코딩은 UTF-8 사용한다.

  • 로그 메시지 하나마다 메일을 보내는 것이 아니고 하나의 메일에는 복수의 로그 메시지가 있다.

  • 설정

 

 

<appender name="SmtpAppender" type="log4net.Appender.SmtpAppender">

   

  <!-- 메일을 받을 곳의 주소 -->

  <to value="to@domain.com" />

   

  <!-- 메일을 보내는 곳의 주소 -->

  <from value="from@domain.com" />

   

  <!-- 제목 -->

  <subject value="test logging message" />

   

  <!-- 메일 서버 정보 -->

  <smtpHost value="SMTPServer.domain.com" />

   

  <!-- 메일을 보낼 때의 버퍼 사이즈 -->

  <bufferSize value="512" />

  <lossy value="true" />

  <evaluator type="log4net.Core.LevelEvaluator">

    <threshold value="WARN"/>

  </evaluator>

   

  <layout type="log4net.Layout.PatternLayout">

    <conversionPattern value="%newline%date [%thread] %-5level %logger - %message%newline" />

  </layout>

</appender>

 

Filter

  • 특정 appender에서만 로그 레벨을 바꾸고 싶은 경우 <appender> 요소 내에서 <filter> 요소를 추가하여 조정할 있다.
  • 로그 레벨을 FATAL 때만 남기고 싶다면 다음과 같이 한다.

 

<filter type="log4net.Filter.LevelRangeFilter">

  <param name="LevelMin" value="FATAL" />

  <param name="LevelMax" value="FATAL" />

</filter>

 

로그 파일 로테이션 시 파일크기+날짜 사용하기

<appender name="debugAppender" type="log4net.Appender.RollingFileAppender">

        <filter type="log4net.Filter.LevelMatchFilter">

            <levelToMatch value="DEBUG" />

        </filter>

        <filter type="log4net.Filter.DenyAllFilter" />

        <File value="Logs\debug.log" />

        <PreserveLogFileNameExtension value="true" />

        <appendToFile value="true" />

        <rollingStyle value="Composite" />

        <maximumFileSize value="32MB" />



로그 파일 이름에 날짜 붙이기

<File value="Logs\perf_.log" />

<staticLogFileName value="false" />

<datePattern value="yyyyMMdd-HHmm" />


perf_201406011140.log 라는 식으로 로그 파일이 만들어진다.



로그 파일 개수 제한

생성된 로그 파일의 최대 개수를 제한 할 수 있다.

<maxSizeRollBackups value="10" />



서식 타입

%literal 리터럴을 표시(「%」을 출력하는 경우 등)

%newline 개행

%logger logger 이름

%class 클래스 이름

%date 실행 시간

%exception 예외 정보

%location 메소드 이름, 행 번호

%line 행 번호

%message 메시지

%method 메소드 이름

%level 로그 레벨

%thread 스레드 ID

%appdomain 실행 파일 이름

%identity 현재 컨텍스트의 유저 이름

(System.Threading.Thread.CurrentPrincipal.Identity.Name 의 값이 설정 되어 있다.)

%username 유저 이름



정보

Log4Net 간단 사용법(Web 포함http://mvcp.tistory.com/404

log4net 사용해 보자 http://nalra999.blog.me/110095620129

[ASP.NET] log4net 설정과 사용법 예제 http://oneprimary.blog.me/30076001926

Apache log4net™ Config Examples   http://logging.apache.org/log4net/release/config-examples.html

신고
by 흥배 2014.01.14 08:00

출처:

http://blog.naver.com/empty_wagon/20151928869

http://blog.naver.com/empty_wagon/20151986599

 

 

사용하는가?

  • Mock 라이브러리는 단위 테스트를 쉽게 하기 위해 존재합니다.
  • 예를 들어서, 클래스 테스트에 데이터베이스를 활용해야 하는 경우가 있다고 가정을 합시다. 경우 직접 데이터베이스에 정보를 넣었다 뺐다 하면 상당히 부담이 됩니다.
  • 이런 부담을 주지 않고, 데이터베이스의 행동을 흉내냄으로써 테스트를 더욱 용이하게 합니다.
  • 하지만 이런 용이함을 얻기 위해서는 인터페이스들에 의해서만 프로그램을 조작하는 설계를 해야 하기는 합니다. (이런 설계가 처음에는 많이 어색하지만, 나중에는 이런 설계가 나은 설계임을 깨닫게 됩니다.)
  • 간단히 이야기해서 Mock 라이브러리는 복잡하고 시간이 걸리는 테스트의 시간을 줄여주기 위해서 존재한다고 있습니다.

 

설치

 

예제

 

public class Book

{

    public string Name { get; set; }

    public int Price { get; set; }

    public string ISBN { get; set; }

}

   

public interface IBookRepository

{

    List<Book> GetBooks();

}

   

public class PriceProcessor

{

    public PriceProcessor(IBookRepository bookRepository)

    {

        bookRepo = bookRepository;

    }

    

    public int GetTotalPrice()

    {

        int result = 0;

               

        foreach (Book b in bookRepo.GetBooks())

        {

            result += b.Price;

        }

    

        return result;

    }

    

    private IBookRepository bookRepo;

}

   

   

[TestFixture]

public class PriceProcessorTests

{

    [Test]

    public void GetTotalPrice_NoDiscount_ReturnCorrectPriceSum()

    {

        List<Book> bookList = new List<Book>()

        {

            new Book() {ISBN="1234", Price=10000, Name="C언어 입문"},

            new Book() {ISBN="1235", Price=20000, Name="C++ 입문"},

            new Book() {ISBN="1236", Price=30000, Name="ASP.NET MVC3 완성"},

        };

    

        Mock<IBookRepository> bookRepoMock = new Mock<IBookRepository>();

        bookRepoMock.Setup(b => b.GetBooks()).Returns(bookList);

    

        PriceProcessor priceProcessor = new PriceProcessor(bookRepoMock.Object);

    

        Assert.That(priceProcessor.GetTotalPrice(), Is.EqualTo(60000));

    }

}

  • bookRepoMock.Setup(b => b.GetBooks()).Returns(bookList);

  • GetBooks함수가 불리면, bookList 반환하라고 명령을 내리고 있습니다. 그리고 완성이 bookRepoMock Object 속성을 이용해서 priceProcessor 넘겨 주고 있습니다

 

함수가 불려졌는지 확인

 

 

public class BestSellers

{

    public BestSellers(IBookRepository bookRepository)

    {

        bookRepo = bookRepository;

    }

    

    public Result AddBook(Book book)

    {

        if (book.Name.Contains("<") || book.Name.Contains(">"))

        {

            return Result.XssAlert;

        }

               

        bookRepo.AddBook(book);

        return Result.Success;

    }

    

    IBookRepository bookRepo;

}

   

public interface IBookRepository

{

    List<Book> GetBooks();

    bool AddBook(Book book);

}

   

   

[Test]

public void AddBook_ContainsTag_IBookRepositoryAddBookIsNotCalled()

{

    Book b = new Book() { Name="<iframe></iframe>"};

    

    Mock<IBookRepository> mock = new Mock<IBookRepository>();

    mock.Setup(r => r.AddBook(b)).Verifiable();

    

    BestSellers bestSellers = new BestSellers(mock.Object);

    

    bestSellers.AddBook(b);

    

    mock.Verify(r => r.AddBook(b), Times.Never());

}

 

Mock 객체에서 생성된 함수들을 매개 변수에 따라 동작을 조절

 

public class Book

{

    public string Name { get; set; }

    public int Price { get; set; }

    public string ISBN { get; set; }

}

   

public void ReducePrice(float reductionRatio)

{

    foreach (Book b in bookRepo.GetBooks())

    {

        b.Price = (int)(reductionRatio * b.Price);

        bookRepo.UpdateBook(b);

    }

}

   

   

[Test]

public void ReducePrice_ValueBetween0And1_IBookRepositoryUpdateBookCalled()

{

    List<Book> bookList = new List<Book>()

    {

        new Book() {ISBN="1234", Price=10000, Name="C언어 입문"},

        new Book() {ISBN="1235", Price=20000, Name="C++ 입문"},

        new Book() {ISBN="1236", Price=30000, Name="ASP.NET MVC3 완성"},

    };

    

    Mock<IBookRepository> bookRepoMock = new Mock<IBookRepository>();

    bookRepoMock.Setup(b => b.GetBooks()).Returns(bookList);

    

    PriceProcessor priceProcessor = new PriceProcessor(bookRepoMock.Object);

    

    priceProcessor.ReducePrice(0.1f);

    

    bookRepoMock.Verify(r => r.UpdateBook(It.IsAny<Book>()),

Times.Exactly(bookList.Count));

}

 

Returs, Callback, Throws

 

public Result AddBook(Book book)

{

    if (book.Name.Contains("<") || book.Name.Contains(">"))

    {

        return Result.XssAlert;

    }

               

    bool added = bookRepo.AddBook(book);

    if (added == false)

    {

        return Result.AlreadyExist;

    }

    return Result.Success;

}

   

   

[Test]

public void AddBook_AddSameBookAgain_ReturnsAlreadyExist()

{

    Book b = new Book() { ISBN = "1234", Name = "abc" };

    

    int count = 0;

    Mock<IBookRepository> mock = new Mock<IBookRepository>();

    mock.Setup(r => r.AddBook(b)).Callback(() => count++).Returns(() => count <= 1);

    

    BestSellers bestSellers = new BestSellers(mock.Object);

    

    Result result1 = bestSellers.AddBook(b);

    Result result2 = bestSellers.AddBook(b);

    

    Assert.That(result1, Is.EqualTo(Result.Success));

    Assert.That(result2, Is.EqualTo(Result.AlreadyExist));

}

 

참고

  • Moq活用して.NETでモックを使ったテストを

 

 

 

 

신고
by 흥배 2014.01.08 08:00

유닛테스트 준비

  • 보통 프로그램이나 라이브러리 프로젝트에 추가 프로젝트로 유닛테스트 프레임워크를 사용하는 프로젝트를 만든다.
    • 이렇게 하면 유닛테스트에 영향을 받지 않으면서 기존처럼 개발을 있다.

  • 유닛테스트 프로젝트에 테스트 메소드를 만들면 테스트 탐색기에 리스트로 나온다.

 

유닛테스트 초기화와 실행 순서

 

지정한 유닛테스트만 실행

  • 유닛테스트 리스트에서 특정 테스트만 실행하고 싶은 경우 항목만 선택 테스트를 실행한다

 

카테고리

  • 유닛테스트를 비슷한 항목끼리 카테고리로 묶어서 카테고리 별로 테스트 있다.

 

유닛테스트 예제

 

 

[TestCategory("ScoreCalculator")]

[TestMethod]

public void Test주고_받는_점수_계산하기()

{

    PrivateType TestObject = new PrivateType(typeof(YakuLibNET.ScoreCalculator));

   

    bool Is부모가_이긴 = true;

    int 이긴_플레이어_점수 = 0;

    object Result;

    YakuLibNET.KUK_END_SCORE ResultScore;

   

   

    // [테스트-부모가 이긴 경우]

    Is부모가_이긴 = true;

    이긴_플레이어_점수 = 3900;

   

    Result = TestObject.InvokeStatic("주고_받는_점수_계산하기", Is부모가_이긴, 이긴_플레이어_점수);

    ResultScore = (YakuLibNET.KUK_END_SCORE)Result;

    Assert.AreEqual(3900, ResultScore.이긴_플레이어가_받는_점수);

    Assert.AreEqual(1300, ResultScore.부모윈_진_자식이_내는_점수);

   

   

    Is부모가_이긴 = true;

    이긴_플레이어_점수 = 2000;

   

    Result = TestObject.InvokeStatic("주고_받는_점수_계산하기", Is부모가_이긴, 이긴_플레이어_점수);

    ResultScore = (YakuLibNET.KUK_END_SCORE)Result;

    Assert.AreEqual(이긴_플레이어_점수, ResultScore.이긴_플레이어가_받는_점수);

    Assert.AreEqual(700, ResultScore.부모윈_진_자식이_내는_점수);

   

   

    Is부모가_이긴 = true;

    이긴_플레이어_점수 = 7700;

   

    Result = TestObject.InvokeStatic("주고_받는_점수_계산하기", Is부모가_이긴, 이긴_플레이어_점수);

    ResultScore = (YakuLibNET.KUK_END_SCORE)Result;

    Assert.AreEqual(이긴_플레이어_점수, ResultScore.이긴_플레이어가_받는_점수);

    Assert.AreEqual(2600, ResultScore.부모윈_진_자식이_내는_점수);

   

   

    Is부모가_이긴 = true;

    이긴_플레이어_점수 = 11600;

   

    Result = TestObject.InvokeStatic("주고_받는_점수_계산하기", Is부모가_이긴, 이긴_플레이어_점수);

    ResultScore = (YakuLibNET.KUK_END_SCORE)Result;

    Assert.AreEqual(이긴_플레이어_점수, ResultScore.이긴_플레이어가_받는_점수);

    Assert.AreEqual(3900, ResultScore.부모윈_진_자식이_내는_점수);

   

   

    Is부모가_이긴 = true;

    이긴_플레이어_점수 = 10600;

   

    Result = TestObject.InvokeStatic("주고_받는_점수_계산하기", Is부모가_이긴, 이긴_플레이어_점수);

    ResultScore = (YakuLibNET.KUK_END_SCORE)Result;

    Assert.AreEqual(이긴_플레이어_점수, ResultScore.이긴_플레이어가_받는_점수);

    Assert.AreEqual(3600, ResultScore.부모윈_진_자식이_내는_점수);

   

   

    // [자신이 이긴 경우]

    Is부모가_이긴 = false;

    이긴_플레이어_점수 = 1300;

   

    Result = TestObject.InvokeStatic("주고_받는_점수_계산하기", Is부모가_이긴, 이긴_플레이어_점수);

    ResultScore = (YakuLibNET.KUK_END_SCORE)Result;

    Assert.AreEqual(이긴_플레이어_점수, ResultScore.이긴_플레이어가_받는_점수);

    Assert.AreEqual(700, ResultScore.자식윈_진_부모가_내는_점수);

    Assert.AreEqual(400, ResultScore.자식윈_진_자식이_내는_점수);

   

   

    Is부모가_이긴 = false;

    이긴_플레이어_점수 = 5200;

   

    Result = TestObject.InvokeStatic("주고_받는_점수_계산하기", Is부모가_이긴, 이긴_플레이어_점수);

    ResultScore = (YakuLibNET.KUK_END_SCORE)Result;

    Assert.AreEqual(이긴_플레이어_점수, ResultScore.이긴_플레이어가_받는_점수);

    Assert.AreEqual(2600, ResultScore.자식윈_진_부모가_내는_점수);

    Assert.AreEqual(1300, ResultScore.자식윈_진_자식이_내는_점수);

   

   

    Is부모가_이긴 = false;

    이긴_플레이어_점수 = 7100;

   

    Result = TestObject.InvokeStatic("주고_받는_점수_계산하기", Is부모가_이긴, 이긴_플레이어_점수);

    ResultScore = (YakuLibNET.KUK_END_SCORE)Result;

    Assert.AreEqual(이긴_플레이어_점수, ResultScore.이긴_플레이어가_받는_점수);

    Assert.AreEqual(3600, ResultScore.자식윈_진_부모가_내는_점수);

    Assert.AreEqual(1800, ResultScore.자식윈_진_자식이_내는_점수);

}

 

테스트 함수 리스트

 

public 아닌 메소드 테스트 하기

 

 

[TestMethod]

public void Test주고_받는_점수_계산하기()

{

        // ScoreCalculator는 private 멤버 함수이다.

    PrivateType TestObject = new PrivateType(typeof(YakuLibNET.ScoreCalculator));

       

    // [테스트-부모가 이긴 경우]

    bool Is부모가_이긴 = true;

    int 이긴_플레이어_점수 = 3900;

   

    object Result = TestObject.InvokeStatic("주고_받는_점수_계산하기", Is부모가_이긴, 이긴_플레이어_점수);

    YakuLibNET.KUK_END_SCORE ResultScore = (YakuLibNET.KUK_END_SCORE)Result;

    Assert.AreEqual(3900, ResultScore.이긴_플레이어가_받는_점수);

    Assert.AreEqual(1300, ResultScore.부모윈_진_자식이_내는_점수);

       

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

public class PrivateAccess

{

    private static string NameStatic

    {

        get { return typeof(PrivateAccess).Name; }

    }

       

        private string Name { get; set; }

   

    public PrivateAccess(string name)

    {

        this.Name = name;

    }

       

        private int Add(int lhs, int rhs)

    {

        return lhs + rhs;

    }

   

    private static int AddStatic(int lhs, int rhs)

    {

        return lhs + rhs;

    }

    private int Subtract(int lhs, int rhs)

    {

        return lhs - rhs;

    }

}

   

   

[TestMethod()]

public void PrivateAccessConstructorTest()

{

    // Arange

    PrivateObject po = new PrivateObject(typeof(PrivateAccess), new object[] { "こんにちわ" });

    string name = "こんにちわ";

    // Act

    // PrivateAccess target = new PrivateAccess(name);

    // Assert

    Assert.AreEqual(name, po.GetProperty("Name"));  

}

   

public void AddTest()

{

    PrivateObject po = new PrivateObject(typeof(PrivateAccess), new object[] { "こんにちは" });

       

    //PrivateAccess_Accessor target = new PrivateAccess_Accessor(param0);

    int lhs = 2;

    int rhs = 3;

    int expected = 5;

    int actual;

    actual = (int) po.Invoke("Add", new object[]{lhs, rhs});

    Assert.AreEqual(expected, actual);

}

   

public void AddStaticTest()

{

    PrivateType pt = new PrivateType(typeof(PrivateAccess));

    int lhs = 1;

    int rhs = 2;

    int expected = 3;

    int actual;

    actual = (int) pt.InvokeStatic("AddStatic", new object[] { lhs, rhs });

    Assert.AreEqual(expected, actual);

}

   

[TestMethod()]

[DeploymentItem("SampleLibrary.dll")]

public void NameTest()

{

    PrivateObject po = new PrivateObject(new PrivateAccess("hello"));

       

    // PrivateAccess_Accessor target = new PrivateAccess_Accessor(param0);

    string expected = "hello";

    string actual = po.GetProperty("Name") as string;

    Assert.AreEqual(expected, actual);

}

   

[TestMethod()]

public void NameStaticTest()

{

    PrivateType pt = new PrivateType(typeof(PrivateAccess));

    string actual;

    actual = pt.GetStaticProperty("NameStatic") as string;

    Assert.AreEqual("PrivateAccess", actual);

}

  • static 클래스의 private 필드 설정하기

 

 

 

 

 

 

 

 

 

 

 

 

public class UniqueNumberRepository

{

    const int MaxStockCount = 200000;

    const int MinStockCount = 50000;

    static Int64 seqNumber = 0;

    static Int64 lastNumber = 0;

}

   

   

PrivateType TestObject = new PrivateType(typeof(UniqueNumberRepository));

TestObject.SetStaticField("seqNumber", 1);

TestObject.SetStaticField("lastNumber", 10);

 

System.Transactions 사용한 DB 유닛테스트

  • 다중 커밋을 지원.
  • 유닛테스트 코드에서 트랜잭션을 시작하여 제품 코드를 호출하고, 제품 코드측에서 데이터베이스에 변경을 커밋하여도 뒤의 유닛테스트 측에서 롤백하여 데이터베이스에 변경을 남기지 않는다.

 

예외 발생 여부 테스트

// InvalidOperationException 이 발생하면 테스트 성공

[TestMethod()]

[ExpectedException(typeof(InvalidOperationException))]

public void TestMethod() 

{

 // 여기서 InvalidOperationException 예외가 발생하는지 테스트

}





참고


신고
by 흥배 2014.01.07 08:00

SignalR 이란?

  • 최신 ASP.NET에 추가된 라이브러리.

이것을 사용하면 리얼타임, 비동기, 쌍방향 통신(Push나 RPC) 기능을 Web 애플리케이션에 쉽게 사용할 수 있다.

  • NuGet을 통해서 입수할 수 있다. Open Source
  • 클라이언트/서버 간에 영속적 연결. 일반 웹은 비 영속적 연결
  • 트랜스포트를 자동으로 네고에이션.

웹 서버나 클라이언트에서 따라서 WebSocket, Server-Send Events, Forever Frames, Long Polling 사용

  • 하나의 서버 당 수천의 접속을 비동기로 처리한다.
  • 비슷한 기술로 Node.js가 있다.
  • ASP.NET 개발팀의 David Fowler, Damian Edwards가 개인 프로젝트로 시작했다가 정식으로 ASP.NET에 들어갔음.

 

 

Sample

  • ASP.NET SignalR Stock Ticker Sample

http://chackr.azurewebsites.net/SignalR.Sample/StockTicker.html

  • SignalR

ShootR https://github.com/NTaylorMullen/ShootR

 

RPC 통신

  • WebSocket, HTTP 위에서 동작하는 RPC(리모트 프로시져 콜)을 구현.
  • 클라이언트에서 서버에 만들어진 함수를 호출하는 느낌으로 서버측 API를 실행할 수 있다.
  • SignalR은 클라이너트에서 호출 가능한 함수 집합을 Hub라고 부른다.

클라이언트에서는 허브의 프록시 오브젝트를 만들어서 함수를 호출하는 형식이다.

  • 물론 함수의 반환 값도 얻을 수 있다. 다만 통신은 비동기 이므로 반환 값은 JQuery로 만들어진 Deferred 오브젝트를 사용하여 얻는다.

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

<script type="text/javascript">

   $(function() {

      var connection = $.hubConnection();

      var sample = connection.createHubProxy("sample");

   

      connection.start(function () {

        sample.invoke("Say", "Hello, world");

      });

   })

</script>

   

<script type="text/javascript">

    $(function() {

        var connection = $.hubConnection();

        var sample = connection.createHubProxy("sample");

   

        connection.start(function () {

            sample.invoke("Say", "Hello, world").done(function(result) {

                alert(result);

            });

        });

    })

</script>

 

]PersistentConnection Hub

  • PersistentConnection Hub 개의 API 있다.
  • PersistentConnection 이름대로 서버와 클라이언트 간의 '지속적인 접속' 제공하면서 클라이언트를 관리하는 기능을 가지고 있다. 통신 기능면에서는 하나의 메시지를 송수신하는 기능 밖에 없는 레벨 API 이다.
  • Hub PersistentConnection 위에 구현된 것으로 레벨 API이다.

클라이언트/서버 간의 함수 호출이라는 기본적인 기능을 제공한다. HubPiprline 이용하여 클라이언트 인증을 구현. 물론 자체 확장도 가능하다.

  • 보통 PersistentConnection 보다 Hub API 사용한다.
  • Hub RPC 사용하기 때문에 PersistentConnection 비교하면 리퀘스트 해석이나 리플렉션을 사용하기 때문에 오버헤드가 크다.
  • 성능을 원한다면 PersistentConnection, 빠른 개발을 원한다면 Hub 사용한다.
    Hub에 정의한 RPC는 비동기로 호출되므로 공유 리소스는 스레드 세이프 하지 않다

Hub

  • 구현

  • Hub 클래스에 구현되어 있는 프로퍼티
    • Context

현재 요청 정보를 유지

  •  
  • Groups

그룹을 관리하는 클래스

  •  
  • Clients

SignalR에서 관리하고 있는 모든 클라이언트를 뜻한다

  •  
  • Caller

리퀘스트를 요청한 클라이언트를 뜻한다.

 

서버와 클라이언트 접속 흐름

  • SignalR 클라이언트 관리

클라이언트 마다 유일한 커넥션 ID 발행 

  • 서버에 준비된 함수를 호출한다

클라이언트는 반환 값을 얻을 있다 

  • 클라이언트에 등록된 함수를 호출한다.

서버는 반환 값을 얻을 없다. 

  • 클라이언트를 그룹화 하여 관리

  • 그룹에 접속을 추가하는 것을 구현

  • 그룹에 함수 호출

 

다양한 클라이언트 지원

  • SignalR JavaScript 이외의 클라이언트에서도 사용할 있다.
  • Windows 8 RT, .NET Framework, iOS, Mac

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

class Program

{

    static void Main(string[] args)

    {

        Start();

    }

   

    private static async void Start()

    {

        var connection = new HubConnection("http://example.com/signalr");

        var sample = connection.CreateHubProxy("sample");

   

        await connection.Start();

   

        var result = await sample.Invoke<string>("Say", "Hello, world");

   

        Console.WriteLine(result);

    }

}

 

 

개발 환경

  • Visual Studio 사용한다.
  • Visual Studio 2012 IIS Express WebSocket 대응.
  • SignalR IIS 아닌 일반 .NET 애플리케이션에서 서버로 사용 가능.
  • 물론 무료용 Visual Studio에서도 개발할 있다.
  • Windows Azure Web 사이트에 배포 가능
  • WebSocket 사용하기 위해서는 Window Server 2012(Windows 8) 이상에서 지원하는 최신 IIS + .NET Framework 4.5 이상을 사용해야 한다.

 

Scale Out

  • 영속적인 연결을 인스턴스 마다 생성
    • 단순하게 수를 늘리는 것만으로 대응할 없다

  • 인스턴스간에 접속을 공유해야 한다
    • 메시징을 이용한다

  • SignalR Scale Out 하기 위한 방법으로 Redis, ServiceBus (Azure), SQL Server 사용한다.

모두 NuGet 통해 입수 있다.

 

Scale Out - Pub/Sub 메시징

  • Windows Azure 서비스 버스
    • 메시지 흐름

 

 

SignalR vs Node.js

  • 기능적으로 비슷하다.
  • Node.js JavaScript 사용하고, SignalR C# Visual Basic.NET 사용.
  • Windows 플랫폼 프로그래머, 한국의 게임 프로그래머에게는 SignalR 사용이 쉬움.
  • JavaScript 알고 있거나 혹은 앞으로 자주 사용한다면 Node.js 좋은 선택이 되지만 그렇지 않다면 (게임 프로그래머의 경우) C# Windows 플랫폼 사용이 훨씬 쉽기 때문에 SignalR 선택하는 것이 좋음.
  • Windows 라이센스 문제는 로직 부분에만 사용하면 문제가 안되고, 특히 클라우드 플랫폼을 사용한다면 크게 문제 되지 않음. Linux 그냥 사용할 있는 쉬운 OS 아님.
  • Node.js npm 통해 풍부한 외부 라이브러리를 사용할 있고, SignalR NuGet 통해 풍부한 외부 라이브러리를 사용할 있다.
  • SignalR Mono 사용하여 Linux에서 사용할 있다.

 

SignalR Node.js 비교표

 

SignalR async/await

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

[HubName("web")]

public class WebHub : Hub

{

    public string GetTitle(string url)

    {

        var client = new WebClient();

   

        var contents = client.DownloadString(url);

   

        var match = Regex.Match(contents, @"<title>(.*)<\/title>");

   

        return match.Success ? match.Groups[1].Value : "";

    }

}

   

   

[HubName("web")]

public class WebHub : Hub

{

    public async Task<string> GetTitle(string url)

    {

        var httpClient = new HttpClient();

   

        var contents = await httpClient.GetStringAsync(url);

   

        var match = Regex.Match(contents, @"<title>(.*)<\/title>");

   

        return match.Success ? match.Groups[1].Value : "";

    }

}

 

SignalR 장점

  • 간단한 클라이언트/서버 API
  • 비동기멀티 스레드로 동작
  • 최신최적의 통신 방법을 자동으로 선택
  • Scale Out 아주 쉬움
  • 클라이언트 그룹 관리 제공
  • 프로그래머는 로직 구현에만 집중할 있다

 

Signal 프로젝트 생성

기타

  • a.html 에서 연결을 b.html 이동하면 연결이 끊어진다.
    • SignalR 하나의 웹페이지에서 관련 작업을 해야 한다.
    • 다만 ASP.NET RAZOR 같은 것을 사용하면 하나의 html 페이지에서 복수의 html 페이지를 포함해서 파일에서 구현하는데 문제 없음

 

\

 

글은

http://www.atmarkit.co.jp/ait/articles/1303/19/news099.html 여기의 자료를 많이 참고했습니다.

 

 

참고

  • SignalR

https://github.com/SignalR/SignalR 
wiki https://github.com/SignalR/SignalR/wiki

  • MSDN

http://msdn.microsoft.com/ko-kr/library/jj943739(v=vs.111).aspx 
http://www.asp.net/signalr

  • Sample

https://github.com/SignalR/Samples

  • SignalR Online Counter Sample

http://www.codeproject.com/Articles/536514/SignalRplusOnlineplusCounterplusSample

  • SignalR - Simple Chat Application in C#

http://www.codeproject.com/Articles/524066/SignalR-Simple-Chat-Application-in-Csharp

  • ASP.NET SignalR 시작하기 (C#)

http://www.taeyo.pe.kr/Columns/View.aspx?SEQ=458&PSEQ=35

신고
by 흥배 2013.12.16 10:16
저작자 표시
신고
by 흥배 2013.12.12 08:00
저작자 표시
신고
by 흥배 2013.12.06 08:00

C#에서 키워드를 식별자로 사용하고 싶을 때 verbatim identifier라는 것을 사용한다.

verbatim identifier는 식별자로 사용하고 싶은 키워드의 앞에 @을 붙이는 것이다.

 

예를 들면 int를 식별자로 사용하고 싶다면

var @int = 10;

 

@는 특별한 의미를 가지지 않으므로 아래와 같은 경우는 에러가 된다.

var @i = 10;

var i = 20;

에러 이유는 i를 중복 정의했기 때문이다.

 

verbatim identifier을 사용하는 경우는 대부분

- 다른 언어와 연동

- 자동 생성

 

 

ASP.NET의 경우 html class라는 식별자와 구분하기 위해 사용한다.

<%: Html.ActionLink("", "Index", null, new { @class = "Navigator" }) %>

 

 

참조: http://bleis-tift.hatenablog.com/entry/20101208/1291817386

저작자 표시
신고
by 흥배 2013.12.04 12:38

C++에서는 로그를 남기기 위해 로그 라이브러리를 만들 때 꼭 사용하는 것이 가변인수와 매크로입니다.

가변인수를 사용하여


void LogMessage( LPCTSTR format, ... )

{

char SBody[ MAX_PATH ] = { 0, };

va_list strlist;

va_start( strlist, format );

_vsntprintf_s( SBody, _countof(SBody), _TRUNCATE, format, strlist );

va_end( strlist );

}



이런 식으로 하나의 메시지를 만들고

이 LogMessage함수를 어디서, 언제 호출했는지 알리는 방법은 __FILE__, __LINE__, __FUNCTION__ 와 같은 매크로를 사용합니다.


이런 기능을 닷넷에서 구현하려면 가변인수는 다음과 같은 방식으로 구현 합니다. 


public void print(int i, params string[] messages)

{

string logmsg;


foreach (string msg in messages)

logmsg += msg;


}



그리고 __FILE__, __LINE__, __FUNCTION__ 매크로는 닷넷의 어트리뷰트를 사용합니다.

using System.Runtime.CompilerServices;

using System.Diagnostics;


void LogToUI(string logText,

[CallerFilePath] string fileName = "",

[CallerMemberName] string methodName = "",

[CallerLineNumber] int lineNumber = 0)

{

string logmsg = string.Format("[줄:{0}|호출:{1}|시간{2}] : {3}", lineNumber, methodName, DateTime.Now.ToShortTimeString(), logText);

listBoxLog.Items.Add(logmsg);

}


// 사용 

LogToUI("서버 생성 성공");

저작자 표시
신고
by 흥배 2013.12.02 08:00
  • 오픈소스 http 클라이언트 라이브러리
  • 공식 사이트 http://restsharp.org/
  • GitHub https://github.com/restsharp/RestSharp
  • 소개 http://pawel.sawicz.eu/restsharp/
  • 예제 http://stackoverflow.com/questions/10226089/restsharp-simple-complete-example




1) 시작 번호 + 크기까지의 숫자범위를 문자로 얻는다.

var client = new RestSharp.RestClient(ServerConfig.LogServerURI); //http://localhost:10301/GameService 

string RestAPI = string.Format("{0}/{1}", "RequestRangeNumber, 10);

var request = new RestSharp.RestRequest(RestAPI, RestSharp.Method.GET);

var queryResult = client.Execute<List<string>>(request).Data;


// 답변 포맷은 "10_20 "

var TokenString = queryResult[0].Split(new Char[] { '_' }, StringSplitOptions.RemoveEmptyEntries);




2) 요청 데이터를 클래스 사용, 답변도 클래스로 얻기

var reqData = new REQ_DATA();

reqData.ID = IDText;

reqData.AreaID = AreaIDText);

var client = new RestSharp.RestClient(URLText);  //http://localhost:10101/Service 

string RestAPI = string.Format("{0}", APIText);  // RequestData


var request = new RestSharp.RestRequest(RestAPI, RestSharp.Method.POST);

request.RequestFormat = RestSharp.DataFormat.Json;

request.AddBody(reqData);


var queryResult = client.Execute(request);

var ResponData = Newtonsoft.Json.JsonConvert.DeserializeObject<ServiceLogic.RES_DATA>(queryResult.Content);

저작자 표시
신고
by 흥배 2013.11.25 08:00
| 1 2 3 4 5 6 7 |

티스토리 툴바