Give the following function:
public class UnderTest
{
public bool Foo(Bar input)
{
if(input.State != State.Paid)
throw new Exception();
return true;
}
}
Whats the best way to test input.State != State.Paid given that State is an enum? I came up with the following. However, this will not catch if a new enum value is added. Is there a better way to test this or should I only care about a single test?
[Theory]
[InlineData(State.New)]
[InlineData(State.Cancelled)]
[InlineData(State.Complete)]
public void NotPaidBar_ThrowsException(State state)
{
// Arrange
var bar = new Bar()
{
State = state
};
var underTest = new UnderTest();
// Act
Action result = () => underTest.Foo(bar);
// Assert
result
.ShouldThrow<Exception>();
}
It is important to consider that unit-tests will not ensure that your program is correct, but only that it isn't broken as per your definitions.
As for your particular question, if you're using TDD with triangulation, and if you stumble upon a new test that doesn't force you to write any new production code, then I would feel that the extra test is not useful from a productivity standpoint.
You can test all the states with a simple loop if you want to, even those that would be added later to the enum:
public void NotPaidBar_ThrowsException()
{
var allStates = Enum.GetValues(typeof (State)).Cast<State>();
foreach (var state in allStates.Except(new[]{State.Paid}))
{
// Arrange
var bar = new Bar()
{
State = state
};
var underTest = new UnderTest();
// Act
Action result = () => underTest.Foo(bar);
// Assert
result.ShouldThrow<Exception>();
}
}
Related
I use MS-Test, moq 4.18.2 and FileSystem (System.IO.Abstractions) 17.0.24 for my tests.
I think I wrote a correct test for InfoLoader_LoadInfoAsync. But, I don't understand how to write a test for MyViewModel::StartLoadInfoAsync to check that InfoList was populated correctly. It seems that I have to duplicate instantiation and configuration of InfoLoader as I did in InfoLoader_LoadInfoAsync. Is there a way around this? How such things are usually tested?
public abstract class IInfoLoader
{
public event Action<MyInfo> InfoLoaded;
public abstract Task LoadInfoAsync();
protected void OnInfoLoaded(MyInfo info)
{
InfoLoaded?.Invoke(info);
}
}
public class InfoLoader : IInfoLoader
{
private readonly IFileSystem _fileSystem;
private readonly string _path;
public InfoLoader(string path, IFileSystem fileSystem) {...}
public async override Task LoadInfoAsync()
{
foreach (var data in await _fileSystem.File.ReadAllLinesAsync(_path))
OnInfoLoaded(new MyInfo(...));
}
}
public class MyViewModel
{
private IInfoLoader _infoLoader;
public ObservableCollection<MyInfo> InfoList { get; }
public MyViewModel(IInfoLoader infoLoader) { ... }
public Task StartLoadInfoAsync()
{
_infoLoader.InfoLoaded += (info) => InfoList.Add(info);
return _infoLoader.LoadInfoAsync();
}
}
Tests
[TestMethod]
public async Task InfoLoader_LoadInfoAsync_Success()
{
var path = "...";
var lines = new string[] { "name1", "name2" };
var expectedInfoList = new List<MyInfo>();
foreach(var line in lines)
expectedInfoList.Add(new MyInfo(line));
var fileSystem = new Mock<IFileSystem>();
fileSystem.Setup(fs => fs.File.ReadAllLinesAsync(path, CancellationToken.None))
.ReturnsAsync(lines);
var actualInfoList = new List<MyInfo>();
var infoLoader = new InfoLoader(path, fileSystem.Object);
infoLoader.InfoLoaded += (info) => actualInfoList.Add(info);
await infoLoader.LoadInfoAsync();
// Assert that items in expectedInfoList and actualInfoList are equal
}
[TestMethod]
public async Task MyViewModel_StartLoadInfoAsync_Success()
{
var expectedInfoList = new List<MyInfo>();
// WHAT DO I DO HERE? DO I CREATE AND CONFIGURE infoLoader LIKE in "InfoLoader_LoadInfoAsync" TEST?
var vm = new MyViewModel(infoLoader.Object);
await vm.StartLoadInfoAsync();
actualInfoList = vm.InfoList;
// Assert that items in expectedInfoList and actualInfoList are equal
}
Since the view model depends on the IInfoLoader abstraction, it can be mocked to behave as expected when the desired member is invoked.
Review the comments in the following example
[TestMethod]
public async Task MyViewModel_StartLoadInfoAsync_Success() {
//Arrange
var info = new MyInfo();
List<MyInfo> expectedInfoList = new List<MyInfo>() { info };
// WHAT DO I DO HERE?
var dependency = new Mock<IInfoLoader>(); //mock the dependency
dependency
// When LoadInfoAsync is invoked
.Setup(_ => _.LoadInfoAsync())
// Use callback to raise event passing the custom arguments expected by the event delegate
.Callback(() => dependency.Raise(_ => _.InfoLoaded += null, info))
// Then allow await LoadInfoAsync to complete properly
.Returns(Task.CompletedTask);
MyViewModel subject = new MyViewModel(dependency.Object);
//Act
await subject.StartLoadInfoAsync();
//Assert
List<MyInfo> actualInfoList = subject.InfoList;
actualInfoList.Should().NotBeEmpty()
And.BeEquivalentTo(expectedInfoList); //Using FluentAssertions
}
Note how a Callback is used to capture when LoadInfoAsync is invoked by the subject so that an event can be raised by the mock, allowing the subject under test to flow to completion as desired
Reference MOQ Quickstart: Events
In order to test StartLoadInfoAsync you need an instance of MyViewModel, so you should:
Create this instance.
Invoke the method StartLoadInfoAsync.
Assert that its state is according to what you need.
Now obviously you have a dependency, which is InfoLoader, so you have two options:
Create and configure a new instance of InfoLoader
Mock InfoLoader so you can test MyViewModel independently of InfoLoader.
The second approach is what you may want to follow, this way you do not need to configure again InfoLoader, mock the FileSystem and so on.
You only need to create a mock of InfoLoader and setup its calls, just like you did with the FileSystem.
I got a class which looks like below
public interface ILocationProvider
{
bool IsRequiredLocation (string type);
}
public class MyClass : IMyInterface
{
private readonly IEnumerable<ILocationProvider> _locationProvider;
public MyClass (ILocationProvider[] locationProvider)
{
_locationProvider = locationProvider;
}
public ILocationProvider ProvideRequireLocationObject(string type)
{
ILocationProvider location = _locationProvider.FirstOrDefault(x => x.IsRequiredLocation(type));
return location;
}
}
Now I am trying to write some tests for it. But I stuck passing the Mock<IEnumerable<ITransitReportCountryFlowProvider>> to constructor. Below my test code
[TestClass]
public class TMyClassTest
{
private Mock<IEnumerable<ILocationProvider>> _locationProvider = null;
private IMyInterface _myClass = null;
[TestInitialize]
public void InitializeTest ()
{
_locationProvider = new Mock<IEnumerable<ILocationProvider>>();
}
[TestMethod]
public void ProvideRequireLocationObject_Test1()
{
//Given: I have type as 'PMI'
string type = "PMI";
//When: I call MyClass object
_myClass = new MyClass(_locationProvider.Object); //wrong actual argument as the formal argument is an array of ILocationProvider
//_locationProvider.Setup(x => x.IsRequiredCountryFlow(It.IsAny<string>())).Returns(true); //how do I setup
ILocationProvider result = _myClass.ProvideRequireLocationObject(type);
//Then: I get a type of ILocationProvider in return
Assert.IsTrue(result is ILocationProvider);
}
}
Problem 1: The line _myClass = new MyClass(_locationProvider.Object) in above test class, as the constructor's formal argument is ILocationProvider[] so I cannot pass a mocking object of Mock<IEnumerable<ILocationProvider>>
Problem 2: If I change the line private readonly IEnumerable<ILocationProvider> _locationProvider; in above MyClass to private readonly ILocationProvider[] _locationProvider; I will not be able to mock it as because mock must be an interface or an abstract or non-sealed class.
Problem 3: How do I set up for _locationProvider.FirstOrDefault(x => x.IsRequiredLocation(type)); in my test method
Problem 4: How do I assert that my method ProvideRequireLocationObject is returning a type of ILocationProvider
First of all, you don’t need to mock the collection. Collections (arrays or lists) are tested well enough to trust on their implementation. Since your constructor expects an array, you need to pass an array. And the simplest way to do that is to simply pass an array. There is no reason to mock this at all.
Changing the implementation details of the class you are testing (as suggested in problem 2) will not change anything on the testing surface. Unit tests should always be independent from the internal implementation details anyway.
How do I assert that my method ProvideRequireLocationObject is returning a type of ILocationProvider
You don’t need to do that. The method has that return type, so the compiler will only accept an implementation where the method returns that type. You are guaranteed by the language that if there is a return value, then it’s of the ILocationProvider type. So you actually just need to check for null.
Taking your implementation, below is a possible way to test this. Note that you don’t actually need to mock this. You usually mock things when the actual implementation is too difficult to set up (e.g. has other dependencies) or when providing a testable implementation is too much work (e.g. an interface with lots of method but you only need one). In this case, I’m assuming that the ILocationProvider is easy to implement, so we’re going to create a test type for this:
[TestClass]
public class MyClassTests
{
[TestMethod]
public void ProvideRequireLocationObject_EmptyCollection()
{
// arrange
var providers = new ILocationProvider[] {};
var obj = new MyClass(providers);
// act
var result = obj.ProvideRequireLocationObject();
// assert
Assert.IsNull(result);
}
[TestMethod]
public void ProvideRequireLocationObject_NoRequiredLocation()
{
// arrange
var providers = new ILocationProvider[] {
new TestLocationProvider(false)
};
var obj = new MyClass(providers);
// act
var result = obj.ProvideRequireLocationObject();
// assert
Assert.IsNull(result);
}
[TestMethod]
public void ProvideRequireLocationObject_OneRequiredLocation()
{
// arrange
var providers = new ILocationProvider[] {
new TestLocationProvider(true)
};
var obj = new MyClass(providers);
// act
var result = obj.ProvideRequireLocationObject();
// assert
Assert.AreEqual(providers[0], result);
}
[TestMethod]
public void ProvideRequireLocationObject_OneRequiredLocationNotFirstInArray()
{
// arrange
var providers = new ILocationProvider[] {
new TestLocationProvider(false),
new TestLocationProvider(true),
new TestLocationProvider(false)
};
var obj = new MyClass(providers);
// act
var result = obj.ProvideRequireLocationObject();
// assert
Assert.AreEqual(providers[1], result);
}
[TestMethod]
public void ProvideRequireLocationObject_MultipleRequiredLocations()
{
// arrange
var providers = new ILocationProvider[] {
new TestLocationProvider(true),
new TestLocationProvider(true),
new TestLocationProvider(true)
};
var obj = new MyClass(providers);
// act
var result = obj.ProvideRequireLocationObject();
// assert
Assert.AreEqual(providers[0], result);
}
public class TestLocationProvider : ILocationProvider
{
public TestLocationProvider(bool isRequiredLocation)
{
IsRequiredLocation = isRequiredLocation;
}
public bool IsRequiredLocation { get; private set; }
}
}
Of course, you could expand those tests as necessary.
I believe are looking at it from the wrong angle. I think you don't need to mock the IEnumerable (Mock<IEnumerable<ITransitReportCountryFlowProvider>>) - IEnumerable has been testing front and back and besides you don't want to have to implement all its logic..
I think you should mock your own classes: Mock<ITransitReportCountryFlowProvider>
And pass a normal IEnumerable containing your mock in it
Something like:
[TestClass]
public class TMyClassTest
{
private Mock<ILocationProvider> _locationProvider = null;
private IEnumerable<ILocationProvider> _locationProviderCollection;
private IMyInterface _myClass = null;
[TestInitialize]
public void InitializeTest ()
{
_locationProvider = new Mock<IEnumerable<ILocationProvider>>();
_locationProviderCollection = new List<ILocationProvider> { _locationProvider };
}
[TestMethod]
public void ProvideRequireLocationObject_Test1()
{
//Given: I have type as 'PMI'
string type = "PMI";
//When: I call MyClass object
_myClass = new MyClass(_locationProviderCollection); //wrong actual argument as the formal argument is an array of ILocationProvider
.....
}
}
I have a testing class
public class TerminationRequestValidation : ValidatorBase<TerminationRequest>
{
public TerminationRequestValidation(IIntHR2BLLContext context) : base(context)
{
}
public override ValidationResult ValidateWithoutThrow(TerminationRequest request)
{
var result = ValidationResult.Success;
/* some logic */
var isHRIAdvanced = Context.Logics.Accessible.HasAccess(request, IntHRSecurityOperationCode.TerminationRequestSetTerminationDateBehindhand);
if (!isHRIAdvanced && Context.Logics.Termination.IsTerminationDateChanged(request))
{
result += CheckTerminationDate(request);
}
return result;
}
public virtual ValidationResult CheckTerminationDate(TerminationRequest request)
{
var result = ValidationResult.Success;
/* any validation logic */
return result;
}
}
I need to check 'CheckTerminationDate' method is performed
[TestMethod]
public void Validate_TerminationDateChangedbyNotAdvanced_TerminationDateCheck()
{
var context = FakeContext.Create();
// first stub
var accessibleBllStub = new Mock<IAccessibleBLL>(MockBehavior.Loose);
accessibleBllStub.Setup(z => z.HasAccess(It.IsAny<TerminationRequest>(), It.IsAny<IntHRSecurityOperationCode>()))
.Returns<TerminationRequest, IntHRSecurityOperationCode>((x, y) => y != IntHRSecurityOperationCode.TerminationRequestSetTerminationDateBehindhand);
context.StubBLL(z => z.Accessible, accessibleBllStub.Object);
// second stub
var terminationBLLStub = new Mock<ITerminationBLL>(MockBehavior.Loose);
terminationBLLStub.Setup(z => z.IsTerminationDateChanged(It.IsAny<TerminationRequest>())).Returns(true);
context.StubBLL(z => z.Termination, terminationBLLStub.Object);
// mock
var validator = new Mock<TerminationRequestValidation>(MockBehavior.Loose, context.MainContext);
// act
validator.Object.ValidateWithoutThrow(termination);
//assert
validator.Verify(z => z.CheckTerminationDate(It.IsAny<TerminationRequest>()));
}
This unit test off course isn't work. On the one hand I need to call real 'ValidateWithoutThrow' method, on the another hand I need to check that stub method 'CheckTerminationDate' is performed.
Guys, help me to find the best solution! May be I need to redesign testing class to make in more testable
I need to check 'CheckTerminationDate' method is performed
You don't. You need to test that the request was validated. Whether that's done inline, or by calling CheckTerminationDate, or by calling some other method, that's an implementation detail - and unit tests don't care about that.
So, your tests should look something like this:
public void ValidateWithoutThrow_ReturnsSucessfulResult_When_RequestIsValid()
{
var validRequest = //...
var validator = new TerminationRequestValidation(/*...*/); // don't mock this class
var result = validator.TerminationRequestValidation(validRequest);
Assert.Equal(ValidationResult.Success, result);
}
public void ValidateWithoutThrow_ReturnsUnsucessfulResult_When_RequestIsInvalid()
{
var invalidRequest = //...
var validator = new TerminationRequestValidation(/*...*/); // don't mock this class
var result = validator.TerminationRequestValidation(invalidRequest);
Assert.NotEqual(ValidationResult.Success, result);
}
As a general rule of thumb, avoid verifying how the method works internally. You're coupling your tests to implementation details and refactoring/maintaining those details will be a living hell.
This is my first post!
I'm trying to write a unit test using nsubstitute but I'm finding the last bit difficult.
I've included a snippet of code below, the test fails when calling the method on the model. Is it possible to stub this method out? Similar to if it was an interface
Cheers guys! Look forward to your responses
James
My unit test attempt
public class MyTests
{
private IModelMapper _modelMapper;
[SetUp]
public void Setup()
{
_modelMapper = Substitute.For<IModelMapper>();
}
[Test]
public void GetModel_Returns_A_Model()
{
var result = theClass.GetModel(new Booking {CurrencyCode = ""}, null);
**UPDATE to include assert**
// Assert
Assert.IsInstance<BasketModel>(result);
}
}
Feature code
public Model GetModel(Booking booking)
{
var model = _modelMapper.Map(booking);
// Is it possible to stub this out? Similar to if it was an interface
model.FormatPricing(somethingHere);
return model;
}
UPDATE - to illustrate return type
BasketModel model = _modelMapper.Map(booking);
UPDATE #2 - to include return
var basketModel = new BasketModel();
BasketModel model = _modelMapper.Map(booking).Returns(basketModel);
Can you include what test failure message you're getting?
Here is the general approach I tend to take for this kind of code. Say we're injecting the IModelMapper into the class-under-test (approximate code; I haven't tested):
[SetUp]
public void Setup()
{
_modelMapper = Substitute.For<IModelMapper>();
theClass = new TheClass(_modelMapper);
}
[Test]
public void GetModel_Returns_Model_From_Mapper()
{
// Arrange
var booking = new Booking { CurrencyCode = "" };
var expectedModel = new BasketModel();
_modelMapper.GetModel(booking).Returns(expectedModel);
// Act
var result = theClass.GetModel(booking, null);
// Assert
Assert.AreSame(expectedModel, result);
}
If you want to stub out BasketModel.FormatModel (that's a big "if". I would recommend using the real type if possible) then you'll want to substitute for BasketModel too.
Be careful - NSubstitute will not work with non-virtual methods, so you may want an interface for BasketModel, or just make sure to use virtual everywhere. (Again, untested code ahead)
[Test]
public void ModelReturnedShouldBeFormatted()
{
// Arrange
var booking = new Booking { CurrencyCode = "" };
var expectedModel = Substitute.For<IBasketModel>();
_modelMapper.GetModel(booking).Returns(expectedModel);
// Act
var result = theClass.GetModel(booking, null);
// Assert
expectedModel.Received().FormatModel(Arg.Any<SomethingHere>());
}
This is testing adherence to a particular contract - TheClass will call FormatModel on the BasketModel returned from the mapper. If you need to duplicate some implementation in the test (again, this is generally discouraged), you can use When..Do:
[Test]
public void FormatModel()
{
// Arrange
var booking = new Booking { CurrencyCode = "" };
var expectedModel = Substitute.For<IBasketModel>();
expectedModel
.When(x => x.FormatModel(Arg.Any<SomethingHere>()))
.Do(/* some action */);
_modelMapper.GetModel(booking).Returns(expectedModel);
// Act
var result = theClass.GetModel(booking, null);
// Assert
// assertion here that depends on "some action" and result
}
Hope this helps.
So I know with TDD you're supposed to write tests first but I can't get my head around how to write a test for the following code. Can someone help me out with at starting point?
private string GetWMIProperty(string property)
{
string value = string.Empty;
SelectQuery selectQuery = new SelectQuery("Win32_OperatingSystem");
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(selectQuery))
{
foreach (ManagementObject mo in searcher.Get())
{
value = mo[property].ToString();
}
}
return value;
}
You'd just write tests for the method's various outcomes, and in doing so you'd define the method's expected behaviour without actually writing the method yet:
[TestMethod]
public MyClass_GetWMIProperty_GivenGoodInput_ReturnsString()
{
var myClass = new MyClass();
var result = myClass.GetWMIProperty("goodinput");
Assert.IsNotNull(result);
}
[TestMethod]
public MyClass_GetWMIProperty_GivenNullInput_ThrowsArgumentNullException()
{
var myClass = new MyClass();
try
{
var result = myClass.GetWMIProperty(null);
}
catch (ArgumentNullException)
{
// Good
return;
}
// Exception not thrown
Assert.Fail();
}
[TestMethod]
public MyClass_GetWMIProperty_GivenBadInput_ReturnsNull()
{
var myClass = new MyClass();
var result = myClass.GetWMIProperty("badinput");
Assert.IsNull(result);
}
Your method would be stubbed as follows:
// Note public/internal so tests can see it
public string GetWMIProperty(string property)
{
// Stubbed
throw new NotImplementedException();
}
These 3 test methods will fail in this state, because NotImplementedException will be thrown and not caught by any of them.
Next you'd write the actual meat of the method so that you can call it in these tests and they'd all pass. The core idea of TDD is that the tests define the behaviour. Here we have defined:
good input returns a string
bad input returns null
null input throws an ArgumentNullException.