No centro da biblioteca está IFileSystem
e FileSystem
. Em vez de chamar métodos como File.ReadAllText
diretamente, use IFileSystem.File.ReadAllText
. Temos exatamente a mesma API, exceto que a nossa é injetável e testável.
dotnet add package TestableIO.System.IO.Abstractions.Wrappers
NOTA: Este pacote NUGET também é publicado como System.IO.Abstractions
, mas sugerimos usar o prefixo para deixar claro que este não é um pacote .NET oficial.
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. " ) ;
}
}
}
A biblioteca também é enviada com uma série de ajudantes de teste para evitar que você precise zombar de cada chamada, para cenários básicos. Eles não são uma cópia completa de um sistema de arquivos da vida real, mas o levará a maior parte do caminho até lá.
dotnet add package TestableIO.System.IO.Abstractions.TestingHelpers
Nota: Este pacote NUGET também é publicado como System.IO.Abstractions.TestingHelpers
, mas sugerimos usar o prefixo para deixar claro que este não é um pacote .NET oficial.
[ 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. " ) ;
}
Até apoiamos o elenco dos tipos não testáveis da estrutura .NET para nossos invólucros testáveis:
FileInfo SomeApiMethodThatReturnsFileInfo ( )
{
return new FileInfo ( " a " ) ;
}
void MyFancyMethod ( )
{
var testableFileInfo = ( FileInfoBase ) SomeApiMethodThatReturnsFileInfo ( ) ;
.. .
}
Desde a versão 4.0, as APIs de nível superior expõem interfaces em vez de classes base abstratas (elas ainda existem), permitindo que você zombe completamente do sistema de arquivos. Aqui está um pequeno exemplo, usando o 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
fornece funcionalidade de conveniência sobre as abstrações do núcleo.
System.IO.Abstractions.Analyzers
fornece analisadores de Roslyn para ajudar a usar abstrações sobre métodos estáticos.
Testably.Abstractions
fornece ajudantes de testes alternativos e abstrações adicionais.