Reputation: 11595
How do I execute an automated test with the option of specifying it as a unit test or "light-weight integration test" without writing the same test twice and only changing the interface it depends on to make it either of the two?
Specifically, I want to execute one test and specify it as a unit test or an integration test. Based on the mode I select, the test should generate a service interface.
Example: Construct testable business layer logic
Upvotes: 0
Views: 160
Reputation: 1
I disagree C Bauer. No consensus here at all. Mocks and dependency injection go a long way to solving this problem. I've seen this approach used more frequently over the last couple of years and it works fine.
Usually in Agile environments where roles are cross functional. Some teams want a single code base/solution to work from. Especially where the size of the code base is relatively small, having "unit" tests able to function as light weight Integration tests works fine. There is no black and white solution here, only the one that works best for the problem at hand. Regardless of what others say there are multiple ways to approach this problem and the solutions/approaches are growing and changing all the time.
Upvotes: 0
Reputation: 11595
Add an entry to the app config.
App Config:
<appSettings>
<add key="IsUnitTest" value="True" />
</appSettings>
Then get the key/value pair from the config file and set your service dependency based on the config value.
Test
[TestClass]
public class MyTest
{
IServices _service = null;
[TestInitialize]
public void Setup()
{
var isUnitTest = bool.Parse(ConfigurationManager.AppSettings["IsUnitTest"]);
if (isUnitTest)
{
_service = new MockService();
}
else
{
_service = new Service();
}
}
. . .
Upvotes: 0
Reputation: 5103
Okay, I think I understand what's happening now.
You want to be able to change the implementation used of an interface at runtime in order to change the location the unit tests run against.
In that case, you want some kind of abstract factory pattern.
Example:
public class ViewModel {
IService _service;
public ViewModel(IServiceFactory factory){
_service = factory.Create();
}
public void SaveWarehouse(Warehouse aWarehouse) {
_service.AddWarehouse(aWarehouse);
}
}
public interface IServiceFactory {
IService Create();
}
public class ProductionFactory : IServiceFactory { //Dependency injected
public IService Create() {
return new ProdService();
}
}
Unit Test:
public class ViewModelTest {
public void when_adding_warehouse() {
//Arrange
var aWarehouse = new WarehouseViewModel { id=1 };
var serviceFactory = new Mock<IServiceFactory>().Object);
var service = new Mock<IService>();
serviceFactory.Setup(factory => factory.Create()).Returns(service.Object);
var viewModel = new ViewModel(serviceFactory.Object);
//Act
viewModel.AddWarehouseCommand(warehouseViewModel);
//Assert
service.Verify(s=>s.AddNewWarehouse(aWarehouse), Times.Once);
}
}
Integration Test:
Your integration test will include a local internal IService implementation and a local internal IServiceFactory implementation returning the local IService implementation. All of your tests will run perfectly fine and you can control where the data goes to very easily.
Upvotes: 0
Reputation: 5103
It doesn't make sense to have a morphic test.
A unit test tests that a single piece of code works in isolation.
An integration tests tests that your code works when integrated into a larger codebase.
For instance, acceptance criteria and psuedocode for unit testing a viewmodel:
public TestMeViewModelTests {
public when_adding_a_warehouse_then_should_call_service_AddNewWarehouse_given_WarehouseModel {
//Arrange
var warehouseViewModel = new WarehouseViewModel { id=1 };
var service = new Mock<IService>();
var interfaceViewModel = new TestMeViewModel(service.Object);
//Act
interfaceViewModel.AddWarehouseCommand(warehouseViewModel);
//Assert
service.Verify(s=>s.AddNewWarehouse(wareHouseViewModel), Times.Once);
}
}
See, there's no cross-pollination of concerns. You're just testing that an idempotent operation is called when adding a new warehouse. If you were using an ORM, then you'd also have unit tests verifying that the dataservice calls are occurring.
If you were going to do an integration test, then your test project would be pointed to a "WarehouseTest" connectionstring that mirrors production, and your integration test might do that same logic, but then check to make sure that the warehouse that is inserted by the test is actually in your DB at the end of the test.
Upvotes: 2