Unit Test-use a simulated object for interaction testing and unit test Interaction
I recently watched the. net unit testing art. I also like unit testing. Here I will explain how to use simulated objects in testing.
During the development process, we will all encounter dependencies between objects, such as dependent databases or files. In this case, we need to use simulated objects for testing. We can manually simulate objects, of course, you can also use a simulated framework.
If there is such a requirement, when a user logs in, I need to verify the user name and password, and then write the user name into the log.
12345678910111213141516171819 |
public class MyLogin { public ILog Log { get ; set ; } public bool Valid( string userName, string passWord) { var isValid = userName == "admin" && passWord == "123456" ; Log.Write(userName); return isValid; } } public interface ILog { void Write( string message); } } |
The above code needs to write a user name to the log after verifying the login information. Because the log writing may depend on files or databases, it may be difficult for us to test, the simulated object is used for testing. The code for handwritten simulated objects is as follows:
1234567891011121314151617181920212223242526272829303132 |
[TestFixture] public class MyLoginTest { [Test] public void Vaild_Test() { MyLogin login = new MyLogin(); var log = new TestLog(); login.Log = log; var userNmae = "admin" ; var passWord = "123456" ; var isLogin = login.Valid(userNmae, passWord); Assert.AreEqual(isLogin, true ); Assert.AreEqual(log.Message, userNmae); } } public class TestLog : ILog { public string Message; public void Write( string message) { this .Message = message; } } |
Here we define a TestLog object, which is a simulated object that inherits the ILog interface. In this test, two tests are performed. Verify that the user name and password are entered correctly. Another option is to verify that the information written to the log by the user is correct (for example, the user name should be written, and the password is written into the log, and the test will fail ).
Here we distinguish between the simulated object and the pile object. In the previous section, we talked about the definition of the pile object. What is the relationship between the simulation object and the pile object?
The Writing Method of the simulated object and the pile object is very different. The key is that the simulated object needs to be asserted, that is, the simulated object can cause the test to fail. The pile object is only defined to facilitate the test and does not need to be asserted. Therefore, the pile object will never cause the test to fail.
In the above test, if we remove the last line of code, that is, we do not write the log assertions, then this object is a pile object.
1 |
Assert.AreEqual(log.Message, userNmae); |
The above simulation object is written by ourselves. It takes a long time to write the simulation object by ourselves. We can use the simulation framework for writing. Here I use the Rhino Mocks framework. To execute the following code, download the Rhino. Mocks. dll file and then directly reference it.
Here I chose the NUnit framework for testing the framework. The test code is as follows:
1234567891011121314151617181920212223242526272829 |
[TestFixture] public class MyLoginTest { [Test] public void Mock_Vaild_Test() { MockRepository mock = new MockRepository(); var log = mock.DynamicMock<ILog>(); var userName = "admin" ; var passWord = "123456" ; using (mock.Record()) { log.Write(userName); } MyLogin login = new MyLogin(); login.Log = log; var isLogin = login.Valid(userName, passWord); Assert.AreEqual(isLogin, true ); mock.VerifyAll(); } |
Here I didn't write a class to inherit the ILog interface. Instead, I dynamically generated an ILog object through the simulation framework. The Code is as follows:
123 |
MockRepository mock = new MockRepository(); var log = mock.DynamicMock<ILog>(); |
The Log object is generated here. To test a simulated object in the recording-Playback mode, you must first define our expected behavior, and finally verify whether the actual behavior is consistent with the expected behavior. Here, we need to record our expected behavior. The Code is as follows:
1234 |
using (mock.Record()) { log.Write(userName); } |
Here we want to write the user name to the log. The Code is as follows:
This method verifies whether the information to be written to the log is consistent with the information actually written to the log. If not, the test fails.
Here we have completed the unit test using the simulated framework. If we do not need to test the log writing method, we can replace the simulated object with a pile object. The method for generating the pile object is as follows:
123 |
MockRepository mock = new MockRepository(); var log = mock.Stub<ILog>(); |
Remove the playback method (mock. VerifyAll () to complete the transformation of the simulation object to the pile object. Note that the recording code is required.
Conclusion: Writing simulation objects and pile objects makes sense. Using a framework can help us simplify unit tests. Generally, a test may contain multiple pile objects, but it is best to have only one simulated object. Too many simulated objects prove that a test method has performed too many tests, which is not conducive to maintaining the test code. Once the code changes, it is easy to make the unit test fail.
In the next section, write some common functions of the testing framework, such as simulating exceptions and simulating return values...