В основе библиотеки лежат IFileSystem
и FileSystem
. Вместо того, чтобы вызывать такие методы, как File.ReadAllText
напрямую, используйте IFileSystem.File.ReadAllText
. У нас есть точно такой же API, за исключением того, что наш инъекционный и тестируемый.
dotnet add package TestableIO.System.IO.Abstractions.Wrappers
Примечание. Этот пакет Nuget также опубликован в качестве System.IO.Abstractions
, но мы предлагаем использовать префикс, чтобы прояснить, что это не официальный пакет .net.
public class MyComponent
{
readonly IFileSystem fileSystem ;
// <summary>Create MyComponent with the given fileSystem implementation</summary>
public MyComponent ( IFileSystem fileSystem )
{
this . fileSystem = fileSystem ;
}
/// <summary>Create MyComponent</summary>
public MyComponent ( ) : this (
fileSystem : new FileSystem ( ) //use default implementation which calls System.IO
)
{
}
public void Validate ( )
{
foreach ( var textFile in fileSystem . Directory . GetFiles ( @"c:" , " *.txt " , SearchOption . TopDirectoryOnly ) )
{
var text = fileSystem . File . ReadAllText ( textFile ) ;
if ( text != " Testing is awesome. " )
throw new NotSupportedException ( " We can't go on together. It's not me, it's you. " ) ;
}
}
}
Библиотека также поставляется с серией тестовых помощников, чтобы спасти вас от необходимости издеваться над каждым вызовом для основных сценариев. Они не являются полной копией реальной файловой системы, но они доставят вас большую часть пути.
dotnet add package TestableIO.System.IO.Abstractions.TestingHelpers
Примечание. Этот пакет Nuget также опубликован как System.IO.Abstractions.TestingHelpers
, но мы предлагаем использовать префикс, чтобы ясно дал понять, что это не официальный пакет .NET.
[ Test ]
public void MyComponent_Validate_ShouldThrowNotSupportedExceptionIfTestingIsNotAwesome ( )
{
// Arrange
var fileSystem = new MockFileSystem ( new Dictionary < string , MockFileData >
{
{ @"c:myfile.txt" , new MockFileData ( " Testing is meh. " ) } ,
{ @"c:demojQuery.js" , new MockFileData ( " some js " ) } ,
{ @"c:demoimage.gif" , new MockFileData ( new byte [ ] { 0x12 , 0x34 , 0x56 , 0xd2 } ) }
} ) ;
var component = new MyComponent ( fileSystem ) ;
try
{
// Act
component . Validate ( ) ;
}
catch ( NotSupportedException ex )
{
// Assert
Assert . That ( ex . Message , Is . EqualTo ( " We can't go on together. It's not me, it's you. " ) ) ;
return ;
}
Assert . Fail ( " The expected exception was not thrown. " ) ;
}
Мы даже поддерживаем кастинг из беспрецедентных типов .NET Framework в наши тестируемые обертки:
FileInfo SomeApiMethodThatReturnsFileInfo ( )
{
return new FileInfo ( " a " ) ;
}
void MyFancyMethod ( )
{
var testableFileInfo = ( FileInfoBase ) SomeApiMethodThatReturnsFileInfo ( ) ;
.. .
}
Поскольку версия 4.0 API верхнего уровня обнажают интерфейсы вместо абстрактных базовых классов (они все еще существуют), что позволяет полностью высмеивать файловую систему. Вот небольшой пример, используя MOQ:
[ Test ]
public void Test1 ( )
{
var watcher = Mock . Of < IFileSystemWatcher > ( ) ;
var file = Mock . Of < IFile > ( ) ;
Mock . Get ( file ) . Setup ( f => f . Exists ( It . IsAny < string > ( ) ) ) . Returns ( true ) ;
Mock . Get ( file ) . Setup ( f => f . ReadAllText ( It . IsAny < string > ( ) ) ) . Throws < OutOfMemoryException > ( ) ;
var unitUnderTest = new SomeClassUsingFileSystemWatcher ( watcher , file ) ;
Assert . Throws < OutOfMemoryException > ( ( ) => {
Mock . Get ( watcher ) . Raise ( w => w . Created += null , new System . IO . FileSystemEventArgs ( System . IO . WatcherChangeTypes . Created , @"C:SomeDirectory" , " Some.File " ) ) ;
} ) ;
Mock . Get ( file ) . Verify ( f => f . Exists ( It . IsAny < string > ( ) ) , Times . Once ) ;
Assert . True ( unitUnderTest . FileWasCreated ) ;
}
public class SomeClassUsingFileSystemWatcher
{
private readonly IFileSystemWatcher _watcher ;
private readonly IFile _file ;
public bool FileWasCreated { get ; private set ; }
public SomeClassUsingFileSystemWatcher ( IFileSystemWatcher watcher , IFile file )
{
this . _file = file ;
this . _watcher = watcher ;
this . _watcher . Created += Watcher_Created ;
}
private void Watcher_Created ( object sender , System . IO . FileSystemEventArgs e )
{
FileWasCreated = true ;
if ( _file . Exists ( e . FullPath ) )
{
var text = _file . ReadAllText ( e . FullPath ) ;
}
}
}
System.IO.Abstractions.Extensions
предоставляет удобные функции поверх основных абстракций.
System.IO.Abstractions.Analyzers
предоставляет анализаторам Roslyn помочь использовать абстракции по сравнению с статическими методами.
Testably.Abstractions