class DomainObject
{
...
}
class View
{
...
}
class MockEnvironment : IEnvironment
{
...
}
With NUnit, you can decorate setup methods with the [SetUp] or [SetUpFixture] attribute. These methods will be run before the tests or test class, allowing you to create data and set this as fields in the test class. A test class might look like this:
public class Test
{
private DomainObject _domainObject;
private View _view;
private MockEnvironment _environment;
[SetUp]
public void CreateStuff()
{
// Create and initialize _domainObject, _view and _environment
}
[Test]
public void SomeMethod_Always_EverythingWorks()
{
// Test stuff and assert, using the class fields
}
}
However, that will cause issues if the tests are run in parallel. Actually, some test frameworks like xUnit has no support for setup methods because of this.
A better approach is to use maker methods that create the data with helper methods:
[Test]
public void SomeMethod_Always_EverythingWorks()
{
var environment = MakeEnvironment(some parameters);
var domainObject = MakeDomainObject();
var view = MakeView(domainObject, environment);
// Do some more initialization
// Implement test
}
private MockEnvironment MakeEnvironment()
{
...
}
private DomainObject MakeDomainObject()
{
...
}
private View MakeView(DomainObject domainObject, IEnvironment environment)
{
...
}
This quickly becomes messy if multiple objects are created. If they depend on each other, it becomes even more messy.
C#7 has a new feature that is very handy for this scenario: return tuples! With return tuples, you can create and initialise everything in one reusable method. Everything is still type safe and very readable:
[Test]
public void SomeMethod_Always_EverythingWorks()
{
var (view, domainObject, environment) = MakeViewAndStuff();
// Implement test
}
private (View view, DomainObject domainObject, MockEnvironment environment) MakeViewAndStuff()
{
var environment = new MockEnvironment();
var domainObject = new DomainObject();
var view = new View(domainObject, environment);
// Do more initialization
return (view, domainObject, environment);
}
Various tests in the test class may or may not reference the objects that are returned. For readability, simply assign the ignored return variables to a dummy underscore variable if you don't need them in the tests:
[Test]
public void SomeOtherMethod_Always_EverythingWorks()
{
var (view, _, _) = MakeViewAndStuff();
// Implement test. No need to use the domain object and environment in this test
}
Clever, huh?