On my mind this week because I’m giving a workshop and a talk on unit testing UI code on iOS.
Tests as Documentation. You think you remember what you did and why… but you don’t.
Replace debugging. I almost never use the debugger. If something isn’t working as expected, I just start writing tests and figuring out what is going on. When the bug is fixed, and I’m berating myself for being stupid enough to write it in the first place, or not figure it out sooner, I also have a bunch of tests to show for the time I spent.
Low energy. Seriously there is no 10x engineer, and none of us function at 100% all of the time. I used to rationalise that I should stop work before I start breaking things. But now when I know I’m not going to be effective at creating I test instead. Maybe I’ll write the thing tomorrow morning, but this afternoon I stub the method and write the tests for it.
No perfection in testing. I’ve mainly seen “we should do it this way it would be better, but that requires us to refactor all the things” argued in places with poor test coverage. I think the point is, do you write a test at all? If a test is stupid, it will take seconds to write and you’ll never need to worry about it again. There is no need to worry about the size of your test binary. Speed, yes, but that is far more determined by integration tests than unit tests.
If you want to improve testing of your iOS apps, I have put together a digital workshop.
I’ve been working on this app which relates to my obsession with color. It’s an image processing app, and you can see some pictures made with it on our Tumblr.
This involved learning about how to take images apart and put them back together, rewriting a lot of stuff in C for performance, etc. But one of the other problems I faced was a question of how to test things involving images? How do I create test images? And how do I compare them?
Creating Test Images
The simplest way to do this is to to draw the image into context. This is super not performant, so isn’t really viable for much other than small test images, but does work.
I have three little helper functions that create some test images that I can work with.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The other thing I have is a function that turns an array of UIColors into an image. This is a bit more complicated, but helpful for some tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This leads me to the question of comparing images. For my purposes (and the app is heavily focused on colors), I can determine if things have worked by comparing two color arrays. I could compare the rawData but I want to abstract it away a bit to make my tests clearer. So I have another function that is basically the inverse of the one above, which extracts an array of pixels from an image.
Turning images into arrays of UIColors and vice versa is so-so performance-wise, and UIColors have a huge space overhead compared to the rawData array. It’s fine for testing, for very small images or a proof of concept, but not much more than that.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Then with two arrays I can just loop through and compare.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Continuing my obsessive testing strategy, I had to set up KIF for UI Automation Tests. Honestly it’s surprising I didn’t do it earlier, but since the UI is very simple I was getting better ROI doing unit tests – I see KIF as a sanity check, not as a way to debug anything.
Step 1: I went through the app with the Accessibility Inspector on and made sure everything had an accessibility label.
Step 2: I made a new test target for KIF tests, and made sure it ran OK. I made a mistake at first and created a Mac testing bundle rather than Cocoa touch (I don’t know why it was even on that screen, I have never made a Mac anything).
Step 5: Trying to debug baffling error messages about pulling in SenTestCase which shouldn’t be required because I’m using XCTest (the default). Adding it as a dependency out of desperation, getting duplicate symbol errors. Some time figuring out what an #ifndef does (basically – if defined). Feeling very discouraged.
Step 6: Pair programming (or, pair-debugging). In desperation, as we have no other ideas, I delete the (theoretically unused) OCUnit targets. Everything works. I am enraged. We are both confused.
Step 7: Check in time! Oh wait, it’s not, because I have edited a submodule.
Picture of my app working after I fixed a bug… using extensive unit tests.
Maybe it’s my functional programming background, but I love static methods. Not everyone gets this, some people parrot the phrase “static methods are bad for testing” but don’t seem to understand what that means.
Static methods are great for testing. Inputs in, outputs out, side effects – what side effects? What’s not to love?
What they are bad for, is mocking. This is an entirely different problem.
Note: here I’m using static to mean methods without side effects, which technically includes Objective-C methods that are denoted with a + (class methods). Turns out, these methods are not technically static, they take place on another object [detailed explanation]. Static methods in Objective-C should properly be C methods.
I’ve been doing a bunch of image processing work lately, and it looks like this:
Break image down into pieces.
Do stuff to it.
Put it back together.
Each of those things individually are great for static methods. Take an image, break it down – do I have the requisite pieces? Put some pieces through this process, is the result right? Given some pieces, do we get an image with the right properties? Wonderful.
I’ve spent hours (OK, days) writing tests for these things, because I was not an expert on image manipulation and in my opinion there is no better way to deeply understand code. I found and fixed a horrible bug. Next, I need to optimise, but my thorough test suite gives me confidence that I can do this refactoring without breaking things, and that I have a good API.
But – I don’t want to deal with this elsewhere in my app. So I absolutely want this stuff to be mockable. So I use a very simple pattern:
Expose a class method that updates the object.
Have a static method that takes whatever information is necessary, and returns whatever needs to be updated.
Class method calls the static method with the requisite information, and updates itself accordingly.
This becomes easy to test. My static method has tests that cover a full range of inputs and outputs. My class method just needs a single test to ascertain that stuff gets updated.
When I’m mocking this object, I just mock the class method. The static one never gets called.
Some people’s irrational loathing for static methods can cause problems in code review. If you don’t want to argue, two options:
Split the static methods out into a “MyClassHelper” object. For larger things I do this to be tidy.
Just don’t declare the methods as static, but use them as such following this pattern – I really don’t like having to do this. But I have done it.
Other People’s Static Methods
The main issue I run into with static methods, is that other people have used them – I find it is common in API design when classes shouldn’t be subclassed.
In this case, I group things together logically in a wrapper. For example, in my current project I have a class that wraps various static things I need for handling images.
Coverage vs Confidence
I am a proponent of code coverage, and regularly use it to guide me to the places in my code base that need a bit more love and understanding. But it’s important to note that coverage doesn’t necessarily mean confidence.
If you have a (say, static!) method that takes a test image and returns an array of length 9, and you verify at the end of it that the array is indeed of length 9, voila you have coverage. What you don’t have, however, is confidence. What are the 9 things?
In part for this reason, I start small and work up incrementally. If I start writing a large test and get confused, I break it down and write a small one first. So in this case I might write 3 tests:
The result has 9 things in it.
The 9 things are correct.
The completion block gets called.
So if I break the test and 9 things are no longer produced, tests 1 and 2 will both fail. If I break it and 9 wrong things are produced, just test 2 will fail. If I accidentally delete the line that calls the completion block, just test 3 will fail.
So when I break something, multiple tests will fail, and the simplest one that is failing will be where I go first to determine what I’ve done.
The other thing about confidence, is that tests should break when you change stuff. The other thing I use to improve my code confidence is when I make a change that doesn’t result in failing tests and I think it should, I go and write some.
TDD Wut?
I don’t often do pure TDD, when testing UI code I expect it would be difficult to the point of not being worthwhile, or in some cases not even possible. Occasionally I TDD, if I’m trying to figure out how something should behave I will start with test cases. More commonly what I do is:
Write the simplest thing.
Write tests for it.
Refine my code, optimise it etc.
I used to employ this strategy a lot when I was writing lots of Guava. I adore functional programming, but a nested for loop is easier to read, understand and debug with print statements if necessary! Because at least initially, it’s nearly as likely that I have made a mistake in my test as in my code.
TL;DR
Static methods are great. Hide them away and they make testing easier, not harder.
Thanks go to Martin, for reviewing a draft of this post.
I was handling a common occurrence when doing something that takes a noticeable amount of time on iOS.
Show loading UI.
Do work.
Transition to post-work UI.
To make the experience better (so that it doesn’t lock up), I pushed 2 onto the background thread, and then had to push 3 back on to the main thread – this is one of The Rules of iOS, all UI stuff has to happen on the main thread.
And then my tests started failing. Aha! A learning opportunity! (Also known as, super annoying as I had other things to get done that day).
You might want to include a handler in the block, this wasn’t necessary for my purposes.
Calling Fulfil
This code is easy! It’s just:
[expectation fulfill];
The question was, where to put it? The examples seemed to put it in a completion block, but if you look back at my outline above, the end of my test was when the UI changes to the post-activity UI. Specifically, when a new ViewController is pushed on the stack.
So, I used a feature of the mocking framework (OCMock), that I can stub a method and then do something when it is called. So here, I stub the pushViewController method, because it’s the last thing that should get called, and when it is I set my expectation to be fulfilled.
Again, I won’t go into mocking here, but you need a mocking framework and some understanding of what mocking is for this to make sense. Currently, I’m using OCMock. Also, XCTest is not the best documented, but here is a handy list of asserts.
This strategy means that for each ViewController there are two classes, MyViewController and MyViewPresenter. This inherits from top level classes which I have imaginatively named ViewController (inheriting from UIViewController) and Presenter (inheriting from NSObject).
ViewController and Presenter
Presenter
The aim of the Presenter class is to expose the things that any ViewController might want to access, making it unnecessary for MyViewController to know about the MyViewPresenter class.
Presenter Interface
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This class handles setting the presenter, ensuring the navigation buttons are set up properly, and that viewLoaded gets called.
ViewController Interface
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Neither of these classes do very much, but they provide us with a way to create a seam which is how we write unit tests. It might seem unnecessary to write tests for these, but that just means that the tests will be quick and simple. I err on the side of if it exists, test it. Both because it’s normally faster to just test it than decide every time, and also because I am often not as smart as I’d like to think I am, therefore am liable to break things.
I’ve opted to use Strict mocks rather than their more forgiving brethren, because I want to know exactly what is going on. This makes the tests a little more brittle than strictly necessary, but I find it a helpful learning mechanism.
PresenterTest
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This is the home screen for an image app, with a simple UI featuring 3 buttons – take a picture, show the gallery, and “inspire” which is not yet implemented.
HomeViewPresenter Interface
The init method is exposed for testing, but the ViewController is instantiated in the app by calling createViewController.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Notice, the view elements are accessed through the views and the actions added to them all call methods in the Presenter itself. The Presenter is also the delegate for the ImagePickerViewController.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The ViewController exposes the view within it, and a method for launching an ImagePickerViewController.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
You can see as a result the ViewController has very little code, because all it is doing is presentation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The tests for the HomeViewController are very simple.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The presenter is a little more interesting. Notice how we capture the action added to the UIButton and call it using sendActionsForControlEvents:.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
When starting from scratch, this method makes it so easy to write unit tests and doesn’t really increase the amount of code required per ViewController, just splits it in two. It’s harder to retrofit to an existing codebase, but it is possible.
Start with the top level classes, and then choose the simplest ViewControllers in your codebase to split. Add tests for them. Then choose progressively more complicated ones. You may need to add more methods to the top level ViewController and Presenter, depending on the complexity of your app. Often the reason why we don’t add tests for ViewControllers is that we never have, so starting is the hardest part.
Finally, on UIAutomation tests, I don’t see this as a replacement for KIF or other UIAutomation tests. These are great for making sure that every screen on the app loads OK, for example, and I still see apps sometimes (especially as apps have got larger) where some unloved corner of the app means that that what should launch a new screen just crashes. However these kind of tests allow us to get into details with less setup than is required by UIAutomation tests, making them easier and less time-consuming to debug.
For more detail on this and other aspects of iOS unit-testing you might find my digital workshop helpful.
Not knowing what it is. Rejecting it as silly without giving it a try. A good try, as there is a learning curve. It will slow you down at first. If you give up before the payoff then you will say “oh that was stupid”.
UI and Networking. On iOS most of what we do is UI and networking, rules out 90% of app, so not really useful.
EBay Fashion app. All test driven.
3 Types of Unit test:
Return value test
State test
Interaction test
Patterns of testing. The Design Patterns book, the Gang of Four never intended it to be the beginning and end of design patterns.
Not going to be rocket since. About getting through the barrier. Writing unit tests after if necessary, but ideally before.
Return Value Test:
Arrange: set up object.
Act: Call method that returns a value.
Assert: Compare against expected value.
With this alone, you should be able to get a very far distance. Onboarding engineers at Facebook, teach them not to be shy about extracting stand alone functions. Helps overcome that barrier.
State test:
Arrange: Set up object.
Act: Call method.
Assert: Compare against expected value.
Since interested in a side effect, just need an additional call to verify state. Should be able to write quite a few tests with these two techniques.
Interaction Test:
Don’t need to be isolated units. They can be connected, as long as they are fast. Check that the system under test (SUT) is communicating correctly to something else.
Don’t want to talk to the real thing:
Takes too long.
Might not be there.
May not have everything (don’t want to use things up).
Might want to test the failure (normal end to end tests).
Want a fake thing that the test can control. Need dependency injection, if the middle thing is creating the end thing, it’s hard to test.
Dependency Injection:
Extract and Override.
Method injection.
Property injection.
Constructor injection.
Difference between having a singleton, and a single way to access a singleton. E.g. NSUserDefaults. Don’t want to access it in this way.
TDD was working for me in a greenfield project, but how many of us get to stay in such a place?
Seams
Make a cut – subclass, override “userDefaults”, do what you want. Very powerful. Very effective with legacy code. Very dangerous. Like a drug. But will end up with the bane of testing code, fragile tests, because tests are coupled to implementation.
For getting started, especially with legacy code – good technique.
Method Injection
Better for other things, like calling “[NSDate date]” – will cause havoc with tests. Can swizzle, or just pass in what time you want. Now you will have a method that does more, now it’s tied to any time, not the current time. Helpful as context for injected object is very small. When spans across method, probably want to hang on to it as a property.
Test can inject the fake thing. But what about production code? Can end up with nil. Objective C will be like “whatever”.
Create custom getter with lazy eval. If no value, get the default value.
Inject in constructor – workhorse of dependency injection. Biggest benefit, makes everything explicit.
Can be annoying to have everything explicit. Long chain of dependencies is a code smell – you have too many dependencies.
Even then, you can simplify that, by using a Builder. Builder pattern creates the object you want according to however it is set. Set in any order, or not set and have it have defaults.
Constructor injection is the main one.
Ambient Context. Change something globally. Swizzling is an example of this. You can, sometimes helpful. But dangerous. Have to have your test restore the pre-test condition.
Let’s learn some good things from other people in other disciplines. There are plenty of smart people who are not using Obj-C
Interaction Test
Types of Fakes: The Art of Unit Testing
Stub: Fake that provides a pre-canned answer.
Mock: Recording how it is called by the SUT, so that it can assert.
Difference is which way the test is pointing to make it’s assertion.
Don’t need a DI framework in order to do DI as a concept.
Mocking, if never mocked before don’t use OCMock or OCMockito at first. Use them eventually. Meanwhile, you can make your own fake. Subclass and override all methods. Test Driven iOS development, means don’t have to do that in Obj-C. Dynamic language, supports DuckTyping.
Subclass NSObject. Put the method in. Use a simple property to record the number of calls. Have a fake return value (if unspecified is nil). Capture arguments.
Interesting thing about doing by hand, answers question of “what do we do in swift”. No introspection available to us. Do it by hand, laborious, might cry a little bit, but nothing stopping us.
Now we have a mock, use it. Start writing some tests.
When I started testing iOS apps, which shamefully was not when I started writing iOS apps, I discovered the biggest impediment to thorough testing on iOS was the View Controller, and it’s mix of UI code, and not.
Now I’m working on my first independent app (yay!) of course I am writing extensive unit tests.
I won’t go into mocking here, but you need a mocking framework and some understanding of what mocking is for this to make sense. Currently, I’m using OCMock. Also, XCTest is not the best documented, and here is a handy list of asserts.
Introducing The Presenter
Step 1 for writing thorough unit tests is getting all the non-view code out of the ViewController. This goes in the Presenter. So for each View Controller, I know have MyClassViewController and MyClassPresenter.
Over time I have refined this and now I have at a top level “ViewController” and “Presenter” classes. The Presenter knows what ViewController it has, but the MyClassViewController knows nothing, the ViewController merely knows there is a Presenter, and can call some standard methods – viewLoaded:, leftNavigationButtons:, rightNavigationButtons:.
This could be a blog post all on it’s own (maybe it will be soon!) but the point is: get non-view code out of the ViewController.
Perform Selector
This is handy for testing that the right thing happens when a button is pressed. This can be done using performSelector:
For example, if we want to verify that the first and only left navigation button does what we want it to:
I discovered this via StackOverflow and it’s genius. One thing that I want to mock and verify when testing my ViewController is the navigationController property. But it’s up in the super class, and the trick of setValue:forKey: was not working.
So what you can do, is create a partialMockForClass, and then stub the navigationController property. (Here, I’m making sure that the ViewController dismiss… method for use by the presenter, causes the navigationController method to be called)
// Create the mock navigation controller.
id mockNavigationController =
OCMStrictClassMock([UINavigationController class]);
// Create a partial mock for the ViewController.
id mockViewController =
[OCMockObject partialMockForObject:viewController_];
// Stub to return mockNavigationController.
[[[mockViewController expect]
andReturn:mockNavigationController] navigationController];
// Set up expectations.
OCMExpect([mockNavigationController
dismissViewControllerAnimated:YES
completion:nil]);
// Call the method that should trigger them.
[viewController_ dismissViewControllerAnimated:YES
withCompletionBlock:nil];
// Verify!
OCMVerify([mockNavigationController
dismissViewControllerAnimated:YES
completion:nil]);
That’s it!
♥ Happy Unit Testing ♥
For more detail on this and other aspects of iOS unit-testing you might find my digital workshop helpful.
For my interview this week, I was sent a list of things to prep. One of them was sorting algorithms.
The best way for me to remember and understand them is to code them, and I decided to go through the list on Wikipedia and code them in Java – even the recursive ones. I think recursively much better in Haskell but interviewers aren’t always cool when you ask to solve the problem in a non-standard language (I lucked out last time).
Because I was writing a lot of similar code, I created an interface first, so that I could write parametrized test cases. These methods should really be static, but I compromised on that in order to make my testing easier.
public interface Sort {
public abstract void sort(int[] array);
}
This was pretty easy, and allowed me to write a parametrized test case that would run all my tests on anything implementing Sort. So I had some code that looked like this:
private Sort sort;
public TestSort(Sort sort) {
this.sort = sort;
}
@Parameters
public static Collection regExValues() {
return Arrays.asList(new Object[][] {
{ new BubbleSort() },
{ new InsertionSort() },
{ new ShellSort() },
{ new MergeSort() },
{ new QuickSort() },
{ new CountingSort() } });
}
And as I created each new class I just added it to my list of parameters, and all my tests (labeled @Test) would be run on them as well. My tests were pretty repetitive and probably could have been parametrized as well, but because I was testing all these different classes I opted for slightly more code in the tests. (Full test code below)
This approach really reduced my coding time, because once I’d set up the interface and the tests I could get a new class working very easily without using cut and paste. I also determined what I wanted to test: null, empty, one element, two elements sorted, two elements unsorted, odd number of elements, even number of elements, and longer with duplicates. When I added an extra test case part way through, it applied to all my previous classes. Determining the tests in advance was more effective than ad-hoc testing, for example, the odd number of elements test was helpful in catching an off-by-one bug.
It also encouraged me to write better code. I might write the function, understand it, and just have a little bug – but I’d keep at it, because I wanted all the tests to pass before I moved on. I also had one bug that was resulting in an array with just two elements reversed – I didn’t notice that staring at the debug statement, it looked fine. But of course, my tests noticed! And the time when I wrote the algorithm right first time? That was pretty nice! Even if it was the super easy CountingSort.
Credit: flickr / David Singleton
Full test code is below if you have any use for it! Note – parameterized test cases are JUnit 4, and I’ve just used ints rather than a Generic <extends Comparable> approach – if you decide to do that, just change to Integer or anything else that implements Comparable.
import java.util.Arrays;
import java.util.Collection;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class TestSort {
private Sort sort;
public TestSort(Sort sort) {
this.sort = sort;
}
@Parameters
public static Collection regExValues() {
return Arrays.asList(new Object[][] {
{new BubbleSort()},
{new InsertionSort()},
{new ShellSort()},
{new MergeSort()},
{new QuickSort()},
{new CountingSort()}});
}
// null array
@Test(expected=IllegalArgumentException.class)
public void testNullArray() {
sort.sort(null);
}
// empty array
@Test
public void testEmptyArray() {
int[] array = new int[0];
sort.sort(array);
Assert.assertArrayEquals(new int[0], array);
}
// one element array
@Test
public void testOneElementArray() {
int[] array = {42};
int[] test = {42};
sort.sort(array);
Assert.assertArrayEquals(test, array);
}
// two element array ordered
@Test
public void testTwoElementOrdArray() {
int[] array = {7, 42};
int[] test = {7, 42};
sort.sort(array);
Assert.assertArrayEquals(test, array);
}
// two element array unordered
@Test
public void testTwoElementUnordArray() {
int[] array = {42, 7};
int[] test = {7, 42};
sort.sort(array);
Assert.assertArrayEquals(test, array);
}
// odd numbered element array
@Test
public void testOddNoElementsArray() {
int[] array = {42, 68, 9, 7, 100, 36, 27};
int[] test = {7, 9, 27, 36, 42, 68, 100};
sort.sort(array);
Assert.assertArrayEquals(test, array);
}
// even numbered element array
@Test
public void testEvenNoElementsArray() {
int[] array = {42, 68, 9, 7, 100, 36, 27, 99};
int[] test = {7, 9, 27, 36, 42, 68, 99, 100};
sort.sort(array);
Assert.assertArrayEquals(test, array);
}
// array in reverse order
@Test
public void testReverseOrder() {
int[] array = {100, 99, 68, 42, 36, 27, 9, 7};
int[] test = {7, 9, 27, 36, 42, 68, 99, 100};
sort.sort(array);
Assert.assertArrayEquals(test, array);
}
// longer array
@Test
public void testLongerArray() {
int[] array = {13, 14, 94, 33, 82, 25, 59, 94, 65, 23, 45, 27, 73, 25, 39, 10};
int[] test = {10, 13, 14, 23, 25, 25, 27, 33, 39, 45, 59, 65, 73, 82, 94, 94};
sort.sort(array);
Assert.assertArrayEquals(test, array);
}
}
Cookie Consent
We use cookies to improve your experience on our site. By using our site, you consent to cookies.