2 <Mono newbie coders start file>
5 For those who are new to Mono and are impatient to contribute with
6 code (uhh... you are brave!!) here is the document you should read.
9 You will see all Mono hackers say the same (great minds have similar
10 way of thinking): First, DO WRITE TESTS!!!. In order to do that:
12 1.- Start with the NUnit Tests Guidelines.
13 In the cvs they are located at: mcs/class/doc/NUnitGuideli...
15 But wait, this is a document for impatient people. So EVERYTHING
16 should be here. Well, it is.
19 Point "2" is waiting for you inside the guidelines. Hey! this is a
20 Must Read Line by Line Text.
22 -------------------- cut here --------------------
24 Mono NUnit Test Guidelines and Best Practices
26 Authors: Nick Drochak <ndrochak@gol.com>
27 Martin Baulig <martin@gnome.org>
28 Last Update: 2002-03-02
33 This document captures all the good ideas people have had about
34 writing NUnit tests for the mono project. This document will be useful
35 for anyone who writes or maintains unit tests.
38 - mcs/class/README has an explanation of the build process and
39 how it relates to the tests.
40 - http://nunit.sourceforge.net is the place to find out about NUnit
44 If you are new to writing NUnit tests, there is a template you may use
45 to help get started. The file is:
47 mcs/class/doc/TemplateTest.cs
50 (2.- This is the point two!. This file is just after the end of the
51 guidelines. Copy/paste it in another buffer. And keep reading.)
54 Save a copy of this file in the appropriate test subdirecty (see
55 below), and replace all the [text] markers with appropriate
56 code. Comments in the template are there to guide you. You should also
57 look at existing tests to see how other people have written them.
58 mcs/class/corlib/Test/System.Collections/CollectionBaseTest.cs is a
59 small one that might help.
61 (3.- You reached the third point. And as expected, it's just here to
62 tell you that the content of CollectionBaseTest.cs is after the
63 TemplateTest.cs code at the end of these guidelines.)
65 The directory that will contain your new file depends on the
66 assembly/namespace of the class for which you are creating the
67 tests. Under mcs/class there is a directory for each assembly. In each
68 assembly there is a Test directory, e.g. mcs/class/corlib/Test. In the
69 Test directory there are sub-directories for each namespace in the
70 assembly, e.g. mcs/class/corlib/Test/Sytem. Put your new test file in
71 the appropriate sub-directory under Test for the class you are
74 Once your test class is complete, you need to add it to the
75 AllTests.cs file in the same directory as your new test. Add a call to
76 "suite.AddTest()" passing the name of your new test class's suite
77 property as the parameter. You will see examples in the AllTests.cs
78 file, so just copy and paste inside there.
80 Once all of that is done, you can do a 'make test' from the top mcs
81 directory. Your test class will be automagically included in the
82 build and the tests will be run along with all the others.
86 -- Provide an unique error message for Assert()
88 Include an unique message for each Assert() so that when the assert
89 fails, it is trivial to locate the failing one. Otherwise, it may be
90 difficult to determine which part of the test is failing. A good way
91 to ensure unique messages is to use something like #A01, #A02 etc.
95 AssertEquals("array match", compare[0], i1[0]);
96 AssertEquals("array match", compare[1], i1[1]);
97 AssertEquals("array match", compare[2], i1[2]);
98 AssertEquals("array match", compare[3], i1[3]);
102 AssertEquals("#A01", compare[0], i1[0]);
103 AssertEquals("#A02", compare[1], i1[1]);
104 AssertEquals("#A03", compare[2], i1[2]);
105 AssertEquals("#A04", compare[3], i1[3]);
107 Once you used such a number in an Assert(), don't change it later on -
108 people might use it it identify the test in bug reports or in mailing
111 -- Use AssertEquals() to compare things, not Assert().
113 Never compare two values with Assert() - if the test fails, people
114 have no idea what went wrong while AssertEquals() reports the failed
119 Assert ("A01", myTicks[0] == t1.Ticks);
123 AssertEquals ("A01", myTicks[0], t1.Ticks);
127 When writing your testcase, please make sure to provide a constructor
128 which takes no arguments:
130 public class DateTimeTest : TestCase
133 public DateTimeTest() : base ("[MonoTests.System.DateTimeTest]") {}
134 public DateTimeTest (string name): base(name) {}
136 public static ITest Suite
139 TestSuite suite = new TestSuite ();
147 Please keep the namespace within each test directory consistent - all
148 tests which are referenced in the same AllTests.cs must be in the same
149 namespace. Of course you can use subnamespaces as you like -
150 especially for subdirectories of your testsuite.
152 For instance, if your AllTests.cs is in namespace "MonoTests" and you
153 have a subdirectory called "System", you can put all the tests in that
154 dir into namespace "MonoTests.System".
156 -- Test your test with the microsoft runtime
158 If possible, try to run your testsuite with the Microsoft runtime on
159 Windows and make sure all tests in it pass. This is especially
160 important if you're writing a totally new testcase - without this
161 check you can never be sure that your testcase contains no bugs ....
163 Don't worry if you're writing your test on Linux, other people can
164 test it for you on Windows.
166 Sometimes you may discover that a test doesn't show the expected
167 result when run with the Microsoft runtime - either because there is a
168 bug in their runtime or something is misleading or wrong in their
169 documentation. In this case, please put a detailed description of the
170 problem to mcs/class/doc/API-notes and do also report it to the list -
171 we'll forward this to the Microsoft people from time to time to help
172 them fix their documentation and runtime.
174 -------------------- cut here ------------------------
176 -------------------- TemplateTest.cs begins ----------
178 // this is a template for making NUnit tests. Text enclosed in square
179 // brackets (and the brackets themselves) should be replaced by appropiate
182 // [File Name].cs - NUnit Test Cases for [explain here]
184 // [Author Name] ([Author email Address])
186 // (C) [Copyright holder]
189 // these are the standard namespaces you will need. You may need to add more
190 // depending on your tests.
191 using NUnit.Framework;
194 // all test namespaces start with "MonoTests." Append the Namespace that
195 // contains the class you are testing, e.g. MonoTests.System.Collections
196 namespace MonoTests.[Namespace]
199 // the class name should end with "Test" and start with the name of the class
200 // you are testing, e.g. CollectionBaseTest
201 public class [Class to be tested]Test : TestCase {
203 // there should be two constructors for your class. The first one
204 // (without parameters) should set the name to something unique.
205 // Of course the name of the method is the same as the name of the
207 public [Constructor]() : base ("[Namespace.Class]") {}
208 public [Constructor](string name) : base(name) {}
210 // this method is run before each Test* method is called. You can put
211 // variable initialization, etc. here that is common to each test.
212 // Just leave the method empty if you don't need to use it.
213 protected override void SetUp() {}
215 // this method is run after each Test* method is called. You can put
216 // clean-up code, etc. here. Whatever needs to be done after each test.
217 // Just leave the method empty if you don't need to use it.
218 protected override void TearDown() {}
220 // this property is required. You need change the parameter for
221 // typeof() below to be your class.
222 public static ITest Suite {
224 return new TestSuite(typeof([Classname here]));
228 // this is just one of probably many test methods in your test class.
229 // each test method must start with "Test". All methods in your class
230 // which start with "Test" will be automagically called by the NUnit
232 public void Test[Something] {
233 // inside here you will exercise your class and then call Assert()
237 ---------------------- TemplateTest.cs ends --------------
238 ---------------------- CollectionBaseTest.cs begins ------
240 // System.Collections.CollectionBase
241 // Test suite for System.Collections.CollectionBase
244 // Nick D. Drochak II
246 // (C) 2001 Nick D. Drochak II
251 using System.Collections;
252 using NUnit.Framework;
254 namespace MonoTests.System.Collections
257 public class CollectionBaseTest : TestCase
259 public CollectionBaseTest () : base ("System.Collection.CollectionBase testsuite") {}
260 public CollectionBaseTest (String name) : base (name) {}
262 // We need a concrete class to test the abstract base class
263 public class ConcreteCollection : CollectionBase
265 // These fields are used as markers to test the On* hooks.
266 public bool onClearFired;
267 public bool onClearCompleteFired;
269 public bool onInsertFired;
270 public int onInsertIndex;
271 public bool onInsertCompleteFired;
272 public int onInsertCompleteIndex;
274 public bool onRemoveFired;
275 public int onRemoveIndex;
276 public bool onRemoveCompleteFired;
277 public int onRemoveCompleteIndex;
279 public bool onSetFired;
280 public int onSetOldValue;
281 public int onSetNewValue;
282 public bool onSetCompleteFired;
283 public int onSetCompleteOldValue;
284 public int onSetCompleteNewValue;
286 // This constructor is used to test OnValid()
287 public ConcreteCollection()
294 // This constructor puts consecutive integers into the list
295 public ConcreteCollection(int i) {
300 for (j = 0; j< i; j++) {
305 // A helper method to look at a value in the list at a specific index
306 public int PeekAt(int index)
310 return (int) listObj[index];
313 // Mark the flag if this hook is fired
314 protected override void OnClear() {
315 this.onClearFired = true;
318 // Mark the flag if this hook is fired
319 protected override void OnClearComplete()
321 this.onClearCompleteFired = true;
324 // Mark the flag, and save the paramter if this hook is fired
325 protected override void OnInsert(int index, object value)
327 this.onInsertFired = true;
328 this.onInsertIndex = index;
331 // Mark the flag, and save the paramter if this hook is fired
332 protected override void OnInsertComplete(int index, object value)
334 this.onInsertCompleteFired = true;
335 this.onInsertCompleteIndex = index;
338 // Mark the flag, and save the paramter if this hook is fired
339 protected override void OnRemove(int index, object value)
341 this.onRemoveFired = true;
342 this.onRemoveIndex = index;
345 // Mark the flag, and save the paramter if this hook is fired
346 protected override void OnRemoveComplete(int index, object value)
348 this.onRemoveCompleteFired = true;
349 this.onRemoveCompleteIndex = index;
352 // Mark the flag, and save the paramters if this hook is fired
353 protected override void OnSet(int index, object oldValue, object newValue)
355 this.onSetFired = true;
356 this.onSetOldValue = (int) oldValue;
357 this.onSetNewValue = (int) newValue;
360 // Mark the flag, and save the paramters if this hook is fired
361 protected override void OnSetComplete(int index, object oldValue, object newValue)
363 this.onSetCompleteFired = true;
364 this.onSetCompleteOldValue = (int) oldValue;
365 this.onSetCompleteNewValue = (int) newValue;
367 } // public class ConcreteCollection
369 public static ITest Suite {
371 return new TestSuite (typeof(CollectionBaseTest));
375 // Check the count property
376 public void TestCount() {
377 ConcreteCollection myCollection;
378 myCollection = new ConcreteCollection(4);
379 Assert(4 == myCollection.Count);
382 // Make sure GetEnumerator returns an object
383 public void TestGetEnumerator() {
384 ConcreteCollection myCollection;
385 myCollection = new ConcreteCollection(4);
386 Assert(null != myCollection.GetEnumerator());
389 // OnValid disallows nulls
390 public void TestOnValid() {
391 ConcreteCollection myCollection;
393 myCollection = new ConcreteCollection();
395 catch (ArgumentNullException) {
399 // Test various Insert paths
400 public void TestInsert() {
401 ConcreteCollection myCollection;
404 // The constructor inserts
405 myCollection = new ConcreteCollection(numberOfItems);
406 Assert(myCollection.onInsertFired);
407 Assert(myCollection.onInsertCompleteFired);
409 // Using the IList interface, check inserts in the middle
410 IList listObj = myCollection;
411 listObj.Insert(1, 9);
412 Assert(myCollection.onInsertIndex == 1);
413 Assert(myCollection.onInsertCompleteIndex == 1);
414 Assert(myCollection.PeekAt(1) == 9);
417 // Test Clear and it's hooks
418 public void TestClear()
420 ConcreteCollection myCollection;
423 myCollection = new ConcreteCollection(numberOfItems);
424 myCollection.Clear();
425 Assert(myCollection.Count == 0);
426 Assert(myCollection.onClearFired);
427 Assert(myCollection.onClearCompleteFired);
430 // Test RemoveAt, other removes and the hooks
431 public void TestRemove()
433 ConcreteCollection myCollection;
436 // Set up a test collection
437 myCollection = new ConcreteCollection(numberOfItems);
439 // The list is 0-based. So if we remove the second one
440 myCollection.RemoveAt(1);
442 // We should see the original third one in it's place
443 Assert(myCollection.PeekAt(1) == 2);
444 Assert(myCollection.onRemoveFired);
445 Assert(myCollection.onRemoveIndex == 1);
446 Assert(myCollection.onRemoveCompleteFired);
447 Assert(myCollection.onRemoveCompleteIndex == 1);
448 IList listObj = myCollection;
450 // Confirm parameters are being passed to the hooks
451 Assert(myCollection.onRemoveIndex == 0);
452 Assert(myCollection.onRemoveCompleteIndex == 0);
455 // Test the random access feature
456 public void TestSet()
458 ConcreteCollection myCollection;
461 myCollection = new ConcreteCollection(numberOfItems);
462 IList listObj = myCollection;
464 Assert((int) listObj[0] == 99);
465 Assert(myCollection.onSetFired);
466 Assert(myCollection.onSetCompleteFired);
467 Assert(myCollection.onSetOldValue == 0);
468 Assert(myCollection.onSetCompleteOldValue == 0);
469 Assert(myCollection.onSetNewValue == 99);
470 Assert(myCollection.onSetCompleteNewValue == 99);
475 ----------------------- CollectionBaseTest.cs ends --------
477 4.- If you use Emacs, you might want to use the .emacs file and the
478 package developed by Brad Merrill mailto:zbrad@cybercom.net. It
479 will allow you to highlight and indent in C# style in your Emacs
480 editor. (XEmacs will still work but it'll also complain).
482 5.- CSharpDevelop is a GPLed IDE developed by IC#Code. Search for it
483 at sourceforge if you are interested in it.
485 6.- For those who Java: "A comparison of Microsoft's C# programming
486 language to Sun Microsystem's Java Programming language" by Dare
487 Obasanjo is a really good (very complete) text to read.
489 7.- Suggest this point and more, now I can't think of anything more.
493 (c) Jaime Anguiano Olarra.
495 The parts included in this document are property of their respective authors.