2013-11-28 4 views
1

Простой класс с использованием TcpListener (это только представить проблему, отнюдь не этот класс делает никакого практического Sence):NUnit поддерживает TCP соединения живых

using System; 
using System.Net.Sockets; 

namespace NUnitTcp 
{ 
    public class Foo 
    { 
     TcpListener lst; 

     public Foo() 
     { 
      lst = new TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), 9090); 
     } 

     ~Foo() 
     { 
      lst.Stop(); 
     } 

     public void Start() 
     { 
      lst.Start(); 
     } 

     public void Stop() 
     { 
      lst.Stop(); 
     } 

    } 
} 

Простое приложение, которое использует Foo:

using System; 

namespace NUnitTcp 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Foo f = new Foo(); 
      f.Start(); 
     } 
    } 
} 

Это приложение работает нормально, порт выдается, как только приложение заканчивается, и приложение может быть запущено снова! Даже без деструктора в Foo это все равно произойдет!

Простой тест NUnit с Foo:

using System; 
using NUnitTcp; 
using NUnit.Framework; 

namespace NUnitTcpTests 
{ 
    [TestFixture] 
    public class TcpTests 
    { 
     [Test] 
     public void test1() 
     { 
      Foo f = new Foo(); 
      f.Start(); 

      Assert.True(true); 
     } 
    } 
} 

Этот тест будет выполняться только один раз в GUI штуковина NUnit. Любое последующее выполнение этого теста вызовет исключение, сообщив нам, что порт используется. Перезапуск NUnit GUI приведет к освобождению порта.
Вы считаете это ошибкой? Мне кажется, как один ...

КОРРЕКЦИЯ - тест будет случайным образом выбросить исключение, для меня около 70% времени.

ответ

5

Сборщик мусора не является детерминированным. Он только быстро закрывается в первом примере, потому что процесс завершается. Я настоятельно рекомендую вам реализовать IDisposable вместо использования финализации, то вы можете использовать:

using(Foo f = new Foo()) 
{ 
    f.Start(); 
    Assert.True(true); 
} 

уверенным в том, что она будет закрыта в кратчайшие сроки.

С чем-то вроде:

public void Dispose() 
{ 
    if(lst != null) lst.Stop(); 
    lst = null; 
} 
+0

Отлично! это все объясняет! Спасибо –

+0

Я решил добавить предложение [TearDown] к моим модульным тестам, где я всегда останавливаю сервер. Я принимаю ваш ответ, хотя он объясняет проблему и правильное решение. –

2

порт не используется, пока ваш Foo экземпляр не будет собрана сборщиком мусора. Таким образом, вы определили финализатор, для этого потребуется две сборки мусора для финализатора (Foo будет перемещен в очередь финализации во время первой сборки мусора, а финализатор будет , возможно,, который вызывается во время второй сборки мусора). Если вы хотите, чтобы убедиться, что порт будет освобожден, то я предлагаю вам закрыть остановка вручную Foo в TearDown метода:

private Foo _foo; 

[SetUp] 
public void Setup() 
{ 
    _foo = new Foo();; 
} 

[Test] 
public void test1() 
{ 
    _foo.Start(); 
    // Assert 
} 

[TearDown] 
public void TearDown() 
{ 
    if (_foo != null) 
     _foo.Stop(); 
} 

Также было бы неплохо реализовать IDisposable на вашем Foo классе, потому что он использует неуправляемые ресурсы, которые должно быть выпущено:

public class Foo : IDisposable 
{ 
    TcpListener lst; 

    public Foo() 
    { 
     lst = new TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), 9090); 
    } 

    public void Dispose() 
    { 
     lst.Stop(); 
    } 
} 
+0

благодарит за разъяснения –

Смежные вопросы