Witam, rozpocząłem niedawno pracę jako programista i dostałem zadanie napisać testy jednostkowe. Nie wiedziałem nic wcześniej na ich temat tak więc po tygodniu zapoznania się z aplikacją i poczytaniu o testach przystąpiłem do pisania.
Od ranu napotkałem pewien problem ponieważ w Internecie piszą, że powinno operować się na interfejsach. Pozwólcie, że przedstawie trochę kodu:
// Fragment klasy, która chcę przetestować i ciało metody, od której zaczynam.
public class SomeClass
{
public SomeClass(ApplicationDbContext db)
:base(db) // po prostu w klasie nadrzędnej jest ApplicationDbContext db;
{
}
// Metoda, którą chcę przetestować.
public bool AddOrUpdateSomeProperty(SomeOtherClass doc)
{
this.db.SomeProperty.AddOrUpdate(doc); // (1)
try
{
this.db.SaveChanges(); // (2)
return true;
}
catch (Exception)
{
//TODO: logging
return false;
}
}
}
Tak więc testując tą klasę i metodę muszę dla pierwszego przypadku gdy metoda się wykona cała(true):
- Z mockować ApplicationDbContext ponieważ testy są na lokalniej maszynie i nie ma dostępu do bazy danych i serwera.
- (1) muszę zrobić coś z db by ta metoda zwróciła śmieci by test się nie wywalił
- (2) tutaj tak samo muszę ustawić coś by test się nie wywalił
Dla przypadku gdy false:
#(2) musi zwrócić Exception
I właśnie z tym mam problem ponieważ db.SomeProperty to jest DbSet<SomeOtherClass> nie jest to ani interfejs ani składowa wirtualna.
ApplicationDbContext wygląda w fragmencie tak:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<SomeOtherClass> SomeProperty{ get; set; }
}
Próbowałem przy użyciu NSubstitute polecanym na tym forum i na innych lecz w dokumentacji wyczytałem, że używając Substitute na klasach wykonywane są nie wirtualne składowe tak więc wykonują się, nie które metody, które wywalają test ponieważ odwołują się do składników bazy danych i serwera. Więc ten framework chyba odpada. A może Substitute.ForPartOf?
Drugi wziąłem pod młotek Moq i tutaj próbuje stworzyć mock ApplicationDbContext przechodzi ładnie lecz nie mogę sobie poradzić z tym DbSetem oraz SaveChanges ponieważ w kontekście jest SaveChanges() i SaveChanges(string x) i działa tylko dla drugiej wersji a dla pierwszej wywala błąd.
Dla takie testu:
[Fact]
public void AddOrUpdateSomeProperty_Success()
{
// Moq
// arrange
var contextMock = new Mock<ApplicationDbContext>();
var provider = new SomeClassProvider(contextMock.Object);
// act
bool result = provider.AddOrUpdateSomeProperty(new SomeClass());
// assert
Assert.True(result);
}
Wyrzuca wyjątek w punkcie (1) testowanej metody:
An exception of type 'System.ArgumentNullException' occurred in EntityFramework.dll but was not handled in user code
Additional information: Wartość nie może być zerowa.
Po dodaniu:
contextMock.Setup(x => x.ExpensesDocuments.AddOrUpdate(It.IsAny<ExpensesDocument>()));
Wyrzuca w punkcie (1) wyjątek:
An exception of type 'System.NotSupportedException' occurred in Moq.dll but was not handled in user code
Additional information: Expression references a method that does not belong to the mocked object: x => x.ExpensesDocuments.AddOrUpdate<ExpensesDocument>(new[] { It.IsAny<ExpensesDocument>() })
Ktoś może pomóc i powiedzieć co jest nie tak i jak się za to zabrać? Dodam jeszcze, że nie ma możliwości edycji ApplicationDbContext i dodanie interfejsu. Aplikacja w .NET 4.5