I'm new to NSubstitue (and quite new to unit testing in .NET at all). I want to test if my class saves all data in different files for each entry in eg StringDictionary.
Say I have my class DataManipulation.cs
:
using System;
using System.Collections;
using System.Collections.Specialized;
namespace ApplicationName
{
// interface for NSubstitute
public interface IManipulator
{
void saveAllData();
void saveEntry(string entryKey, string entryValue);
}
public class DataManipulator : IManipulator
{
protected StringDictionary _data {get; private set;}
public DataManipulator()
{
_data = new StringDictionary();
}
public void addData(string name, string data)
{
this._data.Add(name, data);
}
public void saveAllData()
{
// potential implementation - I want to test this
foreach (DictionaryEntry entry in this._data)
{
this.saveEntry(entry.Key.ToString(), entry.Value.ToString());
}
}
public void saveEntry(string entryKey, string entryValue)
{
// interact with filesystem, save each entry in its own file
}
}
}
What I want to test: when I call DataManipulator.saveAllData()
it saves each _data
entry in a separate file - meaning it runs saveEntry
number of times that equals to _data.Count
. Is it possible with NSubstitute?
Each time I try to use DataManipulation as tested object and separately as a mock - when I run Received()
I have info that no calls were made.
NUnit test template I want to use:
using System;
using System.Collections.Generic;
using NUnit.Framework;
using NSubstitute;
namespace ApplicationName.UnitTests
{
[TestFixture]
class DataManipulatorTests
{
[Test]
public void saveAllData_CallsSaveEntry_ForEachData()
{
DataManipulator dm = new DataManipulator();
dm.addData("abc", "abc");
dm.addData("def", "def");
dm.addData("ghi", "ghi");
dm.saveAllData();
// how to assert if it called DataManipulator.saveEntry() three times?
}
}
}
Or should I do it in different way?
According to some OOP principles and testing needs you have to introduce a dependency or some construction to create "seam" which will fit good for testing.
Using of another dependency as a mock
It will encapsulate data storage and you will check your assertions against it. I recommend you to read about what's the difference between fake, stub and mock.
Add new storage interface and implementation.
public interface IDataStorage { void Store(string key, string value); } public class DataStorage : IDataStorage { public void Store(string key, string value) { //some usefull logic } }
Use it as dependency (and inject via constructor) in your Manipulator implementation
public class DataManipulator : IManipulator { protected IDataStorage _storage { get; private set; } protected StringDictionary _data { get; private set; } public DataManipulator(IDataStorage storage) { _storage = storage; _data = new StringDictionary(); } public void addData(string name, string data) { this._data.Add(name, data); } public void saveAllData() { // potential implementation - I want to test this foreach (DictionaryEntry entry in this._data) { this.saveEntry(entry.Key.ToString(), entry.Value.ToString()); } } public void saveEntry(string entryKey, string entryValue) { _storage.Store(entryKey, entryValue); } }
Test it
[Test] public void saveAllData_CallsSaveEntry_ForEachData() { var dataStorageMock = Substitute.For<IDataStorage>(); DataManipulator dm = new DataManipulator(dataStorageMock); dm.addData("abc", "abc"); dm.addData("def", "def"); dm.addData("ghi", "ghi"); dm.saveAllData(); dataStorageMock.Received().Store("abc", "abc"); dataStorageMock.Received().Store("def", "def"); dataStorageMock.Received().Store("ghi", "ghi"); //or dataStorageMock.Received(3).Store(Arg.Any<string>(), Arg.Any<string>()); }
Most important here that you have not to test private method call. It's a bad practice! Unit testing is all about of testing of public contract, not private methods, which are more changeable in time. (Sorry, I miss that saveEntry(..) is public)
Using of DataManipulator as a mock
I think it's not a good idea, but... The only way to do that with NSubstitute is to make method saveEntry virtual:
public virtual void saveEntry(string entryKey, string entryValue)
{
//something useful
}
and test it:
[Test]
public void saveAllData_CallsSaveEntry_ForEachData()
{
var dm = Substitute.For<DataManipulator>();
dm.addData("abc", "abc");
dm.addData("def", "def");
dm.addData("ghi", "ghi");
dm.saveAllData();
dm.Received(3).saveEntry(Arg.Any<string>(), Arg.Any<string>());
}
The need to do some method virtual just for testing needs may be not very attractive but..
UPD: read it http://nsubstitute.github.io/help/partial-subs/ for better understanding of NSubstitute.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.